Compare commits

..
2 Commits
Author SHA1 Message Date
Jikoo 21cd1926ae Don't find username on the main thread. Not friendly with partial names. 2014-12-01 16:11:52 -05:00
Jikoo 2a66b2e81b Updated to Spigot 1.8 2014-12-01 14:10:14 -05:00
101 changed files with 2970 additions and 11007 deletions

No files matched your search

-10
View File
@@ -1,10 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "maven"
directory: "/"
schedule:
interval: "monthly"
@@ -1,55 +0,0 @@
name: Auto-merge Dependabot PRs
on:
workflow_run:
workflows: [ "Pull Request" ]
types: [ completed ]
jobs:
merge-dependabot:
if: "github.actor == 'dependabot[bot]'
&& github.event.workflow_run.event == 'pull_request'
&& github.event.workflow_run.conclusion == 'success'"
runs-on: ubuntu-latest
steps:
# Note: this is directly from GitHub's example for using data from a triggering workflow:
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow
- name: 'Download artifact'
uses: actions/github-script@v7
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "pr_number"
})[0];
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/pr_number.zip`, Buffer.from(download.data));
# This might be a useless use of cat, but I'm not sure what shell Actions is going to be running.
- name: Add Pull Number Variable
run: |-
unzip pr_number.zip
echo "PR_NUMBER=$(cat pr_number)" >> "$GITHUB_ENV"
- name: Approve
uses: hmarr/auto-approve-action@v3.2.1
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
pull-request-number: "${{ env.PR_NUMBER }}"
- name: Merge
uses: pascalgn/automerge-action@v0.15.6
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
MERGE_LABELS: "dependencies,java"
MERGE_METHOD: "squash"
PULL_REQUEST: "${{ env.PR_NUMBER }}"
-43
View File
@@ -1,43 +0,0 @@
name: OpenInv CI
on:
push:
branches:
- '**'
tags-ignore:
- '**'
# Enable running CI via other Actions, i.e. for drafting releases and handling PRs.
workflow_call:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: 'maven'
# Install Spigot dependencies if necessary.
- 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@v3
with:
name: dist
path: ./target/OpenInv.jar
- name: Upload API Jar
id: upload-api
uses: actions/upload-artifact@v3
with:
name: api
path: ./api/target/openinvapi*.jar
-39
View File
@@ -1,39 +0,0 @@
name: Draft Github Release
on:
push:
tags:
- '**'
jobs:
run-ci:
uses: Jikoo/OpenInv/.github/workflows/ci.yml@master
draft-release:
needs: [ run-ci ]
runs-on: ubuntu-latest
steps:
# Fetch all history - used to assemble changelog.
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set Release Variables
run: bash ./scripts/set_release_env.sh
- name: Download Artifact
uses: actions/download-artifact@v3
with:
name: dist
path: dist
- name: Create Release
id: create-release
uses: softprops/action-gh-release@v0.1.15
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: ${{ env.VERSIONED_NAME }}
body: ${{ env.GENERATED_CHANGELOG }}
draft: true
prerelease: false
files: ./dist/OpenInv.jar
-37
View File
@@ -1,37 +0,0 @@
name: Release to CurseForge
on:
release:
types: [ released ]
jobs:
curseforge_release:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch Github Release Asset
uses: dsaltares/fetch-gh-release-asset@1.1.1
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 }}"
-22
View File
@@ -1,22 +0,0 @@
name: Pull Request
on:
pull_request:
jobs:
run-ci:
uses: Jikoo/OpenInv/.github/workflows/ci.yml@master
store-dependabot-pr-data:
if: "github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'"
runs-on: ubuntu-latest
steps:
# Note: this is directly from GitHub's example for using data from a triggering workflow:
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow
- name: Store Pull Number
run: |
mkdir -p ./pr
echo ${{ github.event.number }} > ./pr/pr_number
- uses: actions/upload-artifact@v3
with:
name: pr_number
path: pr/
-10
View File
@@ -1,10 +0,0 @@
**/.settings
**/.project
**/.classpath
**/.idea/
**.iml
**/target/
**/bin/
**/out/
**/dependency-reduced-pom.xml
**/pom.xml.versionsBackup
@@ -0,0 +1,3 @@
#Wed Jun 01 17:38:54 EDT 2011
eclipse.preferences.version=1
org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false
+13
View File
@@ -0,0 +1,13 @@
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/>.
-139
View File
@@ -1,139 +0,0 @@
## About
OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) which allows users to open and edit anyone's inventory or ender chest - online or not!
## Features
- **OpenInv**: Open anyone's inventory, even if they're offline.
- Read-only mode! No edits allowed! Don't grant the permission `OpenInv.editinv`
- Cross-world support! Don't grant `OpenInv.crossworld`
- No self-opening! Don't grant `OpenInv.openself`
- Drop items as the player! Place items in the unused slots to the right of the armor to drop them
- **OpenEnder**: Open anyone's ender chest, even if they're offline.
- Read-only mode! No edits allowed! Don't grant `OpenInv.editender`
- Cross-world support! Don't grant `OpenInv.crossworld`
- No opening others! Don't grant `OpenInv.openenderall`
- **SilentContainer**: Open containers without displaying an animation or making sound.
- **AnyContainer**: Open containers, even if blocked by ocelots or blocks.
## Commands
See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Commands).
## Permissions
<table>
<tr>
<th>Node</th>
<th>Description</th>
</tr>
<tr>
<td>OpenInv.*</td>
<td>Gives permission to use all of OpenInv.</td>
</tr>
<tr>
<td>OpenInv.openinv</td>
<td>Required to use /openinv.</td>
</tr>
<tr>
<td>OpenInv.openself</td>
<td>Required to open own inventory.</td>
</tr>
<tr>
<td>OpenInv.editinv</td>
<td>Required to make changes to open inventories.</td>
</tr>
<tr>
<td>OpenInv.openonline</td>
<td>Allows users to open online players' inventories. For compatibility reasons this is granted by the nodes OpenInv.openinv and OpenInv.openender.</td>
</tr>
<tr>
<td>OpenInv.openoffline</td>
<td>Allows users to open offline players' inventories. For compatibility reasons this is granted by the nodes OpenInv.openinv and OpenInv.openender.</td>
</tr>
<tr>
<td>OpenInv.openender</td>
<td>Required to use /openender.</td>
</tr>
<tr>
<td>OpenInv.editender</td>
<td>Required to make changes to open ender chests.</td>
</tr>
<tr>
<td>OpenInv.openenderall</td>
<td>Allows users to open others' ender chests. Without it, users can only open their own.</td>
</tr>
<tr>
<td>OpenInv.exempt</td>
<td>Prevents the player's inventory being opened by others.</td>
</tr>
<tr>
<td>OpenInv.override</td>
<td>Allows bypassing of the exempt permission.</td>
</tr>
<tr>
<td>OpenInv.crossworld</td>
<td>Allows cross-world usage of /openinv and /openender.</td>
</tr>
<tr>
<td>OpenInv.search</td>
<td>Required to use /searchinv and /searchender.</td>
</tr>
<tr>
<td>OpenInv.searchenchant</td>
<td>Required to use /searchenchant.</td>
</tr>
<tr>
<td>OpenInv.anychest</td>
<td>Required to use /anychest.</td>
</tr>
<tr>
<td>OpenInv.any.default</td>
<td>Cause AnyContainer to be enabled by default.</td>
</tr>
<tr>
<td>OpenInv.silent</td>
<td>Required to use /silentcontainer.</td>
</tr>
<tr>
<td>OpenInv.silent.default</td>
<td>Cause SilentContainer to be enabled by default.</td>
</tr>
<tr>
<td>OpenInv.spectate</td>
<td>Allows users in spectate gamemode to edit inventories.</td>
</tr>
</table>
## For Developers
### 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>
```
### Compilation
To compile, the relevant Spigot jars must be installed in the local repository.
As OpenInv is compiled against Mojang's mappings, you must run BuildTools with the `--remapped` argument:
`java -jar BuildTools.jar --remapped --rev $serverVersion`
`$serverVersion` is the version of the server, i.e. `1.18.1`
To compile for a single version, specify the module you are targeting:
`mvn -pl $moduleName -am clean install`
`$moduleName` is the name of the module, i.e. `internal/v1_18_R1`.
To compile for a set of versions, use a profile. Select a profile using the `-P` argument:
`mvn clean package -am -P all`
The only provided profile is `all`. The final file is `target/OpenInv.jar`
For more information, check out the [official Maven guide](http://maven.apache.org/guides/introduction/introduction-to-profiles.html).
-49
View File
@@ -1,49 +0,0 @@
<!--
~ Copyright (C) 2011-2022 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<version>4.4.2</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>
@@ -1,290 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.internal.IInventoryAccess;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.util.InventoryAccess;
import java.util.UUID;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interface defining behavior for the OpenInv plugin.
*/
public interface IOpenInv {
/**
* Check if the server version is supported by OpenInv.
*
* @return true if the server version is supported
*/
boolean isSupportedVersion();
/**
* Check the configuration value for whether 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();
/**
* Check the configuration value for whether OpenInv allows offline access. If true, OpenInv will not load or allow
* modification of players while they are not online. This does not prevent other plugins from using existing loaded
* players who have gone offline.
*
* @return false unless configured otherwise
* @since 4.2.0
*/
boolean disableOfflineAccess();
/**
* Check the configuration value for whether OpenInv uses history for opening commands. If false, OpenInv will use
* the previous parameterized search when no parameters are provided.
*
* @return false unless configured otherwise
* @since 4.3.0
*/
boolean noArgsOpensSelf();
/**
* Get the active {@link IAnySilentContainer} implementation.
*
* @return the active implementation for the server version
* @throws IllegalStateException if the server version is unsupported
*/
@NotNull IAnySilentContainer getAnySilentContainer();
/**
* @deprecated Use static {@link InventoryAccess} methods.
*/
@Deprecated(forRemoval = true)
default @NotNull IInventoryAccess getInventoryAccess() {
return new InventoryAccess();
}
/**
* @deprecated Use {@link #getAnyContainerStatus(OfflinePlayer)}. Not all containers are chests.
*/
@Deprecated(forRemoval = true, since = "4.2.0")
default boolean getPlayerAnyChestStatus(@NotNull OfflinePlayer offline) {
return getAnyContainerStatus(offline);
}
/**
* Get whether a user has AnyContainer mode enabled.
*
* @param offline the user to obtain the state of
* @return true if AnyContainer mode is enabled
*/
boolean getAnyContainerStatus(@NotNull OfflinePlayer offline);
/**
* @deprecated Use {@link #setAnyContainerStatus(OfflinePlayer, boolean)}. Not all containers are chests.
*/
@Deprecated(forRemoval = true, since = "4.2.0")
default void setPlayerAnyChestStatus(@NotNull OfflinePlayer offline, boolean status) {
setAnyContainerStatus(offline, status);
}
/**
* Set whether a user has AnyContainer mode enabled.
*
* @param offline the user to set the state of
* @param status the state of the mode
*/
void setAnyContainerStatus(@NotNull OfflinePlayer offline, boolean status);
/**
* @deprecated Use {@link #getSilentContainerStatus(OfflinePlayer)}. Not all containers are chests.
*/
@Deprecated(forRemoval = true, since = "4.2.0")
default boolean getPlayerSilentChestStatus(@NotNull OfflinePlayer offline) {
return getSilentContainerStatus(offline);
}
/**
* Get whether a user has SilentContainer mode enabled.
*
* @param offline the user to obtain the state of
* @return true if SilentContainer mode is enabled
*/
boolean getSilentContainerStatus(@NotNull OfflinePlayer offline);
/**
* @deprecated Use {@link #setSilentContainerStatus(OfflinePlayer, boolean)}. Not all containers are chests.
*/
@Deprecated(forRemoval = true, since = "4.2.0")
default void setPlayerSilentChestStatus(@NotNull OfflinePlayer offline, boolean status) {
setSilentContainerStatus(offline, status);
}
/**
* Set whether a user has SilentContainer mode enabled.
*
* @param offline the user to set the state of
* @param status the state of the mode
*/
void setSilentContainerStatus(@NotNull OfflinePlayer offline, boolean status);
/**
* Get a unique identifier by which the OfflinePlayer can be referenced.
*
* @deprecated Use {@link OfflinePlayer#getUniqueId()} and {@link UUID#toString()}. This was necessary for non-UUID
* versions of Minecraft, but support for them has been dropped for years.
* @param offline the OfflinePlayer
* @return the identifier
* @throws IllegalStateException if the server version is unsupported
*/
@Deprecated(forRemoval = true)
default @NotNull String getPlayerID(@NotNull OfflinePlayer offline) {
return offline.getUniqueId().toString();
}
/**
* Get an {@link ISpecialEnderChest} for a user.
*
* @param player the {@link Player} owning the inventory
* @param online whether the owner is currently online
* @return the created inventory
* @throws IllegalStateException if the server version is unsupported
* @throws InstantiationException if there was an issue creating the inventory
*/
@NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull Player player, boolean online) throws InstantiationException;
/**
* Get an {@link ISpecialPlayerInventory} for a user.
*
* @param player the {@link Player} owning the inventory
* @param online whether the owner is currently online
* @return the created inventory
* @throws IllegalStateException if the server version is unsupported
* @throws InstantiationException if there was an issue creating the inventory
*/
@NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull Player player, boolean online) throws InstantiationException;
/**
* Open an {@link ISpecialInventory} for a {@link Player}.
*
* @param player the viewer
* @param inventory the inventory to open
* @return the resulting {@link InventoryView}
*/
@Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory);
/**
* Check if a {@link Player} is currently loaded by OpenInv.
*
* @param playerUuid the {@link UUID} of the {@code Player}
* @return whether the {@code Player} is loaded
* @since 4.2.0
*/
boolean isPlayerLoaded(@NotNull UUID playerUuid);
/**
* Load a {@link Player} from an {@link OfflinePlayer}. If the user has not played before or the default world for
* the server is not loaded, this will return {@code null}.
*
* @param offline the {@code OfflinePlayer} to load a {@code Player} for
* @return the loaded {@code Player}
* @throws IllegalStateException if the server version is unsupported
*/
@Nullable Player loadPlayer(@NotNull final OfflinePlayer offline);
/**
* Match an existing {@link OfflinePlayer}. If the name is a {@link UUID#toString() UUID string}, this will only
* return the user if they have actually played on the server before, unlike {@link Bukkit#getOfflinePlayer(UUID)}.
*
* <p>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 string to match
* @return the user with the closest matching name
*/
@Nullable OfflinePlayer matchPlayer(@NotNull String name);
/**
* @deprecated OpenInv uses action bar chat for notifications. Whether they show is based on language settings.
*/
@Deprecated(forRemoval = true)
default boolean notifyAnyChest() {
return true;
}
/**
* @deprecated OpenInv uses action bar chat for notifications. Whether they show is based on language settings.
*/
@Deprecated(forRemoval = true)
default boolean notifySilentChest() {
return true;
}
/**
* @deprecated see {@link #retainPlayer(Player, Plugin)}
*/
@Deprecated(forRemoval = true, since = "4.2.0")
default void releasePlayer(@NotNull Player player, @NotNull Plugin plugin) {}
/**
* @deprecated OpenInv no longer uses an internal cache beyond maintaining copies of currently open inventories.
* If you wish to use/modify a player, ensure either {@link IOpenInv#isPlayerLoaded(UUID)} is false or the player
* instance is the same memory address as the one in use by OpenInv.
* <pre>
* public &#64;NotNull Player savePlayerData(&#64;NotNull Player player) {
* IOpenInv openInv = ...
* if (!openInv.disableSaving() && openInv.isPlayerLoaded(player.getUniqueId())) {
* Player openInvLoadedPlayer = openInv.loadPlayer(myInUsePlayer);
* if (openInvLoadedPlayer != player) {
* // The copy loaded by OpenInv is not the same as our loaded copy. Push our changes.
* copyPlayerModifications(player, openInvLoadedPlayer);
* }
* // OpenInv will handle saving data when the player is unloaded.
* // Optionally, to be sure our changes will persist, save now.
* // openInvLoadedPlayer.saveData();
* return openInvLoadedPlayer;
* }
*
* player.saveData();
* return player;
* }
* </pre>
*/
@Deprecated(forRemoval = true, since = "4.2.0")
default void retainPlayer(@NotNull Player player, @NotNull Plugin plugin) {}
/**
* Forcibly close inventories of and unload any cached data for a user.
*
* @param offline the {@link OfflinePlayer} to unload
*/
void unload(@NotNull OfflinePlayer offline);
Logger getLogger();
}
@@ -1,74 +0,0 @@
package com.lishid.openinv.event;
import com.lishid.openinv.internal.ISpecialInventory;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
/**
* Event fired before OpenInv saves a player's data.
*/
public class OpenPlayerSaveEvent extends Event implements Cancellable {
private static final HandlerList HANDLERS = new HandlerList();
private final Player player;
private final ISpecialInventory inventory;
private boolean cancelled = false;
public OpenPlayerSaveEvent(@NotNull Player player, @NotNull ISpecialInventory inventory) {
this.player = player;
this.inventory = inventory;
}
/**
* Get the {@link Player} whose data is being saved.
*
* @return player the Player whose data is being saved
*/
public @NotNull Player getPlayer() {
return player;
}
/**
* Get the {@link ISpecialInventory} that triggered the save by being closed.
*
* @return the special inventory
*/
public @NotNull ISpecialInventory getInventory() {
return inventory;
}
/**
* Get whether the event is cancelled.
*
* @return true if the event is cancelled
*/
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* Set whether the event is cancelled.
*
* @param cancel whether the event is cancelled
*/
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@NotNull
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
}
@@ -1,179 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
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.inventory.InventoryHolder;
import org.bukkit.util.BoundingBox;
import org.jetbrains.annotations.NotNull;
public interface IAnySilentContainer {
/**
* Forcibly open the container at the given coordinates for the Player. This will open blocked containers! Be sure
* to check {@link #isAnyContainerNeeded(Block)} first if that is not desirable.
*
* @param player the {@link Player} opening the container
* @param silent whether the container's noise is to be silenced
* @param block the {@link Block} of the container
* @return true if the container can be opened
*/
boolean activateContainer(@NotNull Player player, boolean silent, @NotNull Block block);
/**
* Perform operations required to close the current container silently.
*
* @param player the {@link Player} closing a container
*/
void deactivateContainer(@NotNull Player player);
/**
* @param player the player opening the container
* @param block the {@link Block} of the container
* @return true if the container is blocked
* @deprecated use {@link #isAnyContainerNeeded(Block)}
*/
@Deprecated(forRemoval = true, since = "4.1.9")
default boolean isAnyContainerNeeded(@NotNull Player player, @NotNull Block block) {
return isAnyContainerNeeded(block);
}
/**
* Check if the container at the given coordinates is blocked.
*
* @param block the {@link Block} of the container
* @return true if the container is blocked
*/
default boolean isAnyContainerNeeded(@NotNull Block block) {
BlockState blockState = block.getState();
// Barrels do not require AnyContainer.
if (blockState instanceof Barrel) {
return false;
}
// Enderchests require a non-occluding block on top to open.
if (blockState instanceof EnderChest) {
return block.getRelative(0, 1, 0).getType().isOccluding();
}
// Shulker boxes require 1/2 a block clear in the direction they open.
if (blockState instanceof ShulkerBox shulker) {
if (isShulkerBlocked(shulker)) {
return true;
}
}
if (!(blockState instanceof org.bukkit.block.Chest)) {
return false;
}
if (isChestBlocked(block)) {
return true;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Chest chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) {
return false;
}
int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4;
BlockFace relativeFace = BlockFace.values()[ordinal];
org.bukkit.block.Block relative = block.getRelative(relativeFace);
if (relative.getType() != block.getType()) {
return false;
}
BlockData relativeData = relative.getBlockData();
if (!(relativeData instanceof Chest relativeChest)) {
return false;
}
if (relativeChest.getFacing() != chest.getFacing()
|| relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) {
return false;
}
return isChestBlocked(relative);
}
/**
* Check if a {@link ShulkerBox} cannot be opened under ordinary circumstances.
*
* @param shulkerBox the shulker box container
* @return whether the container is blocked
*/
boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox);
/**
* Check if a chest cannot be opened under ordinary circumstances.
*
* @param chest the chest block
* @return whether the container is blocked
*/
default boolean isChestBlocked(@NotNull Block chest) {
org.bukkit.block.Block relative = chest.getRelative(0, 1, 0);
return relative.getType().isOccluding()
|| chest.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0;
}
/**
* Check if the given {@link Block} is a container which can be unblocked or silenced.
*
* @param block the potential container
* @return true if the type is a supported container
*/
default boolean isAnySilentContainer(@NotNull Block block) {
return isAnySilentContainer(block.getState());
}
/**
* Check if the given {@link BlockState} is a container which can be unblocked or silenced.
*
* @param blockState the potential container
* @return true if the type is a supported container
*/
default boolean isAnySilentContainer(@NotNull BlockState blockState) {
return blockState instanceof InventoryHolder holder && isAnySilentContainer(holder)
|| blockState instanceof EnderChest;
}
/**
* Check if the given {@link InventoryHolder} is a container which can be unblocked or silenced.
*
* @param holder the potential container
* @return true if the type is a supported container
*/
default boolean isAnySilentContainer(@NotNull InventoryHolder holder) {
return holder instanceof org.bukkit.block.EnderChest
|| holder instanceof org.bukkit.block.Chest
|| holder instanceof org.bukkit.block.DoubleChest
|| holder instanceof org.bukkit.block.ShulkerBox
|| holder instanceof org.bukkit.block.Barrel;
}
}
@@ -1,62 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import com.lishid.openinv.util.InventoryAccess;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @deprecated Use static {@link InventoryAccess} methods.
*/
@Deprecated(forRemoval = true)
public interface IInventoryAccess {
/**
* @deprecated Use static {@link InventoryAccess} methods.
*/
@Deprecated(forRemoval = true)
default @Nullable ISpecialEnderChest getSpecialEnderChest(@NotNull Inventory inventory) {
return InventoryAccess.getEnderChest(inventory);
}
/**
* @deprecated Use static {@link InventoryAccess} methods.
*/
@Deprecated(forRemoval = true)
default @Nullable ISpecialPlayerInventory getSpecialPlayerInventory(@NotNull Inventory inventory) {
return InventoryAccess.getPlayerInventory(inventory);
}
/**
* @deprecated Use static {@link InventoryAccess} methods.
*/
@Deprecated(forRemoval = true)
default boolean isSpecialEnderChest(@NotNull Inventory inventory) {
return InventoryAccess.isEnderChest(inventory);
}
/**
* @deprecated Use static {@link InventoryAccess} methods.
*/
@Deprecated(forRemoval = true)
default boolean isSpecialPlayerInventory(@NotNull Inventory inventory) {
return InventoryAccess.isPlayerInventory(inventory);
}
}
@@ -1,64 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
/**
* Interface defining behavior for special inventories backed by other inventories' content listings.
*/
public interface ISpecialInventory {
/**
* Get the {@link Inventory} associated with this {@code ISpecialInventory}.
*
* @return the Bukkit inventory
*/
@NotNull Inventory getBukkitInventory();
/**
* Set the owning {@link Player} instance to a newly-joined user.
*
* @param player the user coming online
*/
void setPlayerOnline(@NotNull Player player);
/**
* Mark the owner of the inventory offline.
*/
void setPlayerOffline();
/**
* Get whether the inventory is being viewed by any users.
*
* @return true if the inventory is being viewed
*/
default boolean isInUse() {
return !getBukkitInventory().getViewers().isEmpty();
}
/**
* Get the {@link Player} who owns the inventory.
*
* @return the {@link HumanEntity} who owns the inventory
*/
@NotNull HumanEntity getPlayer();
}
@@ -1,50 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import java.util.List;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.Inventory;
/**
* An {@link ISpecialInventory} representing a player inventory.
*/
public interface ISpecialPlayerInventory extends ISpecialInventory {
/*
* Player inventory usage varies from all other inventories - as the inventory is technically open at all times,
* if the player is online or has been online while the inventory is open, they are in the viewer list.
*/
@Override
default boolean isInUse() {
Inventory inventory = getBukkitInventory();
List<HumanEntity> viewers = inventory.getViewers();
if (viewers.size() != 1) {
return !viewers.isEmpty();
}
HumanEntity viewer = viewers.get(0);
if (!viewer.getUniqueId().equals(getPlayer().getUniqueId())) {
return true;
}
return viewer.getOpenInventory().getTopInventory().equals(inventory);
}
}
@@ -1,123 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.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 final 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");
getInventory = craftInventory.getDeclaredMethod("getInventory");
} catch (ClassNotFoundException | NoSuchMethodException ignored) {}
}
/**
* @deprecated use {@link #isUsable()}
*/
@Deprecated(forRemoval = true)
public static boolean isUseable() {
return isUsable();
}
public static boolean isUsable() {
return craftInventory != null && getInventory != null;
}
/**
* Check if an {@link Inventory} is an {@link ISpecialPlayerInventory} implementation.
*
* @param inventory the Bukkit inventory
* @return true if backed by the correct implementation
*/
public static boolean isPlayerInventory(@NotNull Inventory inventory) {
return getPlayerInventory(inventory) != null;
}
/**
* Get the {@link ISpecialPlayerInventory} backing an {@link Inventory}. Returns {@code null} if the inventory is
* not backed by the correct class.
*
* @param inventory the Bukkit inventory
* @return the backing implementation if available
*/
public static @Nullable ISpecialPlayerInventory getPlayerInventory(@NotNull Inventory inventory) {
return getSpecialInventory(ISpecialPlayerInventory.class, inventory);
}
/**
* Check if an {@link Inventory} is an {@link ISpecialEnderChest} implementation.
*
* @param inventory the Bukkit inventory
* @return true if backed by the correct implementation
*/
public static boolean isEnderChest(@NotNull Inventory inventory) {
return getEnderChest(inventory) != null;
}
/**
* Get the {@link ISpecialEnderChest} backing an {@link Inventory}. Returns {@code null} if the inventory is
* not backed by the correct class.
*
* @param inventory the Bukkit inventory
* @return the backing implementation if available
*/
public static @Nullable ISpecialEnderChest getEnderChest(@NotNull Inventory inventory) {
return getSpecialInventory(ISpecialEnderChest.class, inventory);
}
private static <T extends ISpecialInventory> @Nullable T getSpecialInventory(@NotNull Class<T> expected, @NotNull Inventory inventory) {
Object inv;
if (isUsable() && craftInventory.isAssignableFrom(inventory.getClass())) {
try {
inv = getInventory.invoke(inventory);
if (expected.isInstance(inv)) {
return expected.cast(inv);
}
} catch (ReflectiveOperationException ignored) {}
}
// Use reflection to find the IInventory
inv = ReflectionHelper.grabObjectByType(inventory, expected);
if (expected.isInstance(inv)) {
return expected.cast(inv);
}
return null;
}
/**
* @deprecated Do not create a new instance to use static methods.
*/
@Deprecated(forRemoval = true, since = "4.2.0")
public InventoryAccess() {}
}
@@ -1,75 +0,0 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import java.lang.reflect.Field;
import org.jetbrains.annotations.Nullable;
/**
* A utility for making reflection easier.
*/
public final class ReflectionHelper {
private ReflectionHelper() {}
/**
* Grab an {@link Object} stored in a {@link Field} of another {@code Object}.
*
* <p>This casts the field to the correct class. Any issues will result in a {@code null} return value.
*
* @param fieldType the {@link Class} of {@code Object} stored in the {@code Field}
* @param holder the containing {@code Object}
* @param <T> the type of stored {@code Object}
* @return the first matching {@code Object} or {@code null} if none match
*/
public static <T> @Nullable T grabObjectByType(final Object holder, final Class<T> fieldType) {
Field field = grabFieldByType(holder.getClass(), fieldType);
if (field != null) {
try {
return fieldType.cast(field.get(holder));
} catch (IllegalAccessException ignored) {
// Ignore issues obtaining field
}
}
return null;
}
/**
* Grab a {@link Field} of an {@link Object}
*
* @param fieldType the {@link Class} of the object
* @param holderType the containing {@code Class}
* @return the first matching object or {@code null} if none match
*/
public static @Nullable Field grabFieldByType(Class<?> holderType, Class<?> fieldType) {
for (Field field : holderType.getDeclaredFields()) {
field.setAccessible(true);
if (fieldType.isAssignableFrom(field.getType())) {
return field;
}
}
if (holderType.getSuperclass() != null) {
return grabFieldByType(fieldType, holderType.getSuperclass());
}
return null;
}
}
@@ -1,150 +0,0 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.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(){}
}
-58
View File
@@ -1,58 +0,0 @@
<!--
~ Copyright (C) 2011-2022 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.4.2</version>
</parent>
<artifactId>openinvassembly</artifactId>
<name>OpenInvAssembly</name>
<packaging>pom</packaging>
<build>
<directory>../target</directory>
<finalName>OpenInv</finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>reactor-uberjar</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/assembly/reactor-uberjar.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
-54
View File
@@ -1,54 +0,0 @@
<!--
~ Copyright (C) 2011-2021 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>reactor-uberjar</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<moduleSets>
<moduleSet>
<useAllReactorProjects>true</useAllReactorProjects>
<binaries>
<outputDirectory>/</outputDirectory>
<unpack>true</unpack>
<unpackOptions>
<excludes>
<!--
- Exclude existing meta - assembly will write its own manifest, and the rest is maven
- details that end users don't need. Ignoring it saves around 7KB after compression
- with a single internal module present.
-->
<exclude>META-INF/**</exclude>
</excludes>
</unpackOptions>
<!-- Exclude dependencies - shade plugin will handle their inclusion as necessary in individual jars. -->
<includeDependencies>false</includeDependencies>
</binaries>
</moduleSet>
</moduleSets>
</assembly>
View File
File renamed without changes.
-81
View File
@@ -1,81 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2011-2023 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<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.4.2</version>
</parent>
<artifactId>openinvadapter1_19_R3</artifactId>
<name>OpenInvAdapter1_19_R3</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spigot.version>1.19.4-R0.1-SNAPSHOT</spigot.version>
</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>
<scope>provided</scope>
</dependency>
<dependency>
<artifactId>openinvplugincore</artifactId>
<groupId>com.lishid</groupId>
</dependency>
<dependency>
<artifactId>annotations</artifactId>
<groupId>org.jetbrains</groupId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -1,244 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_19_R3;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.util.ReflectionHelper;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.SimpleMenuProvider;
import net.minecraft.world.entity.monster.Shulker;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
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.ShulkerBoxBlock;
import net.minecraft.world.level.block.TrappedChestBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.ShulkerBox;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class AnySilentContainer implements IAnySilentContainer {
private @Nullable Field serverPlayerGameModeGameType;
public AnySilentContainer() {
try {
try {
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
this.serverPlayerGameModeGameType.setAccessible(true);
} catch (NoSuchFieldException e) {
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!");
logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues");
logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail.");
// 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(@NotNull ShulkerBox shulkerBox) {
org.bukkit.World bukkitWorld = shulkerBox.getWorld();
if (!(bukkitWorld instanceof CraftWorld)) {
bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID());
}
if (!(bukkitWorld instanceof CraftWorld craftWorld)) {
Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!");
OpenInv.getPlugin(OpenInv.class).getLogger().log(Level.WARNING, exception.getMessage(), exception);
return false;
}
final ServerLevel world = craftWorld.getHandle();
final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ());
final BlockEntity tile = world.getBlockEntity(blockPosition);
if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity)
|| shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) {
return false;
}
BlockState blockState = world.getBlockState(blockPosition);
// See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen
AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F)
.move(blockPosition)
.deflate(1.0E-6D);
return !world.noCollision(boundingBox);
}
@Override
public boolean activateContainer(
@NotNull final Player bukkitPlayer,
final boolean silentchest,
@NotNull final org.bukkit.block.Block bukkitBlock) {
// Silent ender chest is API-only
if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) {
bukkitPlayer.openInventory(bukkitPlayer.getEnderChest());
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
ServerPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
final ServerLevel level = player.getLevel();
final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
final BlockEntity blockEntity = level.getBlockEntity(blockPos);
if (blockEntity == null) {
return false;
}
if (blockEntity instanceof EnderChestBlockEntity enderChestTile) {
// Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock
PlayerEnderChestContainer enderChest = player.getEnderChestInventory();
enderChest.setActiveChest(enderChestTile);
player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> {
MenuType<?> containers = PlayerDataManager.getContainers(enderChest.getContainerSize());
int rows = enderChest.getContainerSize() / 9;
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
}, Component.translatable(("container.enderchest"))));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
if (!(blockEntity instanceof MenuProvider menuProvider)) {
return false;
}
BlockState blockState = level.getBlockState(blockPos);
Block block = blockState.getBlock();
if (block instanceof ChestBlock chestBlock) {
// boolean flag: do not check if chest is blocked
menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true);
if (menuProvider == null) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
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 || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) {
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);
// ServerPlayer#closeContainer cannot be called without entering an
// infinite loop because this method is called during inventory close.
// From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent
player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity());
// From ServerPlayer#closeContainer
player.doCloseContainer();
// Regular inventory close will handle the rest - packet sending, etc.
// 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();
}
}
}
@@ -1,143 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_19_R3;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.storage.PlayerDataStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
public class OpenPlayer extends CraftPlayer {
public OpenPlayer(CraftServer server, ServerPlayer entity) {
super(server, entity);
}
@Override
public void loadData() {
// See CraftPlayer#loadData
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
if (loaded != null) {
getHandle().readAdditionalSaveData(loaded);
}
}
@Override
public void saveData() {
ServerPlayer player = this.getHandle();
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
try {
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
setExtraData(playerData);
if (!isOnline()) {
// Preserve certain data when offline.
CompoundTag oldData = worldNBTStorage.load(player);
revertSpecialValues(playerData, oldData);
}
File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
NbtIo.writeCompressed(playerData, file);
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat");
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
Util.safeReplaceFile(file1, file, file2);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
}
}
private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) {
if (oldData == null) {
return;
}
// Prevent vehicle deletion.
if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) {
// See net.minecraft.server.players.PlayerList#save(ServerPlayer)
// See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
try {
Tag attach = oldData.get("Attach");
if (attach != null) {
newData.putUUID("Attach", NbtUtils.loadUUID(attach));
}
} catch (IllegalArgumentException ignored) {
// Likely will not re-mount successfully, but at least the mount will not be deleted.
}
newData.put("Entity", oldData.getCompound("Entity"));
newData.put("RootVehicle", oldData.getCompound("RootVehicle"));
}
// Revert automatic updates to play timestamps.
copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class);
copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class);
copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class);
}
private <T extends Tag> void copyValue(
@NotNull CompoundTag source,
@NotNull CompoundTag target,
@NotNull String container,
@NotNull String key,
@NotNull Class<T> tagType) {
CompoundTag oldContainer = getTag(source, container, CompoundTag.class);
CompoundTag newContainer = getTag(target, container, CompoundTag.class);
// Container being null means the server implementation doesn't store this data.
if (oldContainer == null || newContainer == null) {
return;
}
// If old tag exists, copy it to new location, removing otherwise.
setTag(newContainer, key, getTag(oldContainer, key, tagType));
}
private <T extends Tag> @Nullable T getTag(
@NotNull CompoundTag container,
@NotNull String key,
@NotNull Class<T> dataType) {
Tag value = container.get(key);
if (value == null || !dataType.isAssignableFrom(value.getClass())) {
return null;
}
return dataType.cast(value);
}
private <T extends Tag> void setTag(
@NotNull CompoundTag container,
@NotNull String key,
@Nullable T data) {
if (data == null) {
container.remove(key);
} else {
container.put(key, data);
}
}
}
@@ -1,265 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_19_R3;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.OpenInventoryView;
import com.mojang.authlib.GameProfile;
import java.lang.reflect.Field;
import java.util.logging.Logger;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_19_R3.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlayerDataManager implements IPlayerDataManager {
private @Nullable Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
bukkitEntity = null;
}
}
public static @NotNull ServerPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
ServerPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
}
if (nmsPlayer == null) {
// Could use reflection to examine fields, but it's honestly not worth the bother.
throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation");
}
return nmsPlayer;
}
@Override
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
// Ensure player has data
if (!offline.hasPlayedBefore()) {
return null;
}
// Create a profile and entity to load the player data
// See net.minecraft.server.players.PlayerList#canPlayerLogin
// and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
if (worldServer == null) {
return null;
}
ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
entity.getAdvancements().stopListening();
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// Load data. This also reads basic data into the player.
// See CraftPlayer#loadData
CompoundTag loadedData = server.getPlayerList().playerIo.load(entity);
if (loadedData == null) {
// Exceptions with loading are logged by Mojang.
return null;
}
// Also read "extra" data.
entity.readAdditionalSaveData(loadedData);
if (entity.level == null) {
// Paper: Move player to spawn
// SPIGOT-7340: Cannot call ServerPlayer#spawnIn with a null world
ServerLevel level = null;
Vec3 position = null;
if (entity.getRespawnDimension() != null) {
level = entity.server.getLevel(entity.getRespawnDimension());
if (level != null && entity.getRespawnPosition() != null) {
position = net.minecraft.world.entity.player.Player.findRespawnPositionAndUseSpawnBlock(level, entity.getRespawnPosition(), entity.getRespawnAngle(), false, false).orElse(null);
}
}
if (level == null || position == null) {
level = ((CraftWorld) server.server.getWorlds().get(0)).getHandle();
position = Vec3.atCenterOf(level.getSharedSpawnPos());
}
entity.level = level;
entity.setPos(position);
}
// Return the Bukkit entity.
return entity.getBukkitEntity();
}
void injectPlayer(ServerPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
return openPlayer;
}
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
return player;
}
}
@Nullable
@Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.connection == null) {
return null;
}
InventoryView view = getView(player, inventory);
if (view == null) {
return player.openInventory(inventory.getBukkitInventory());
}
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public MenuType<?> getType() {
return getContainers(inventory.getBukkitInventory().getSize());
}
};
container.setTitle(Component.literal(view.getTitle()));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
return null;
}
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
Component.literal(container.getBukkitView().getTitle())));
nmsPlayer.containerMenu = container;
nmsPlayer.initMenu(container);
return container.getBukkitView();
}
private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) {
if (inventory instanceof SpecialEnderChest) {
return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest");
} else if (inventory instanceof SpecialPlayerInventory) {
return new OpenInventoryView(player, inventory, "container.player", "'s Inventory");
} else {
return null;
}
}
static @NotNull MenuType<?> getContainers(int inventorySize) {
return switch (inventorySize) {
case 9 -> MenuType.GENERIC_9x1;
case 18 -> MenuType.GENERIC_9x2;
case 36 -> MenuType.GENERIC_9x4; // PLAYER
case 41, 45 -> MenuType.GENERIC_9x5;
case 54 -> MenuType.GENERIC_9x6;
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
};
}
@Override
public int convertToPlayerSlot(InventoryView view, int rawSlot) {
int topSize = view.getTopInventory().getSize();
if (topSize <= rawSlot) {
// Slot is not inside special inventory, use Bukkit logic.
return view.convertSlot(rawSlot);
}
// Main inventory, slots 0-26 -> 9-35
if (rawSlot < 27) {
return rawSlot + 9;
}
// Hotbar, slots 27-35 -> 0-8
if (rawSlot < 36) {
return rawSlot - 27;
}
// Armor, slots 36-39 -> 39-36
if (rawSlot < 40) {
return 36 + (39 - rawSlot);
}
// Off hand
if (rawSlot == 40) {
return 40;
}
// Drop slots, "out of inventory"
return -1;
}
}
@@ -1,350 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_19_R3;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.ContainerListener;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
private ServerPlayer owner;
private NonNullList<ItemStack> items;
private boolean playerOnline;
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
super(PlayerDataManager.getHandle(player));
this.inventory = new CraftInventory(this);
this.owner = PlayerDataManager.getHandle(player);
this.playerOnline = online;
this.items = this.owner.getEnderChestInventory().items;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
if (this.playerOnline) {
return;
}
ServerPlayer offlinePlayer = this.owner;
ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player);
// Set owner to new player.
this.owner = onlinePlayer;
// Set player's ender chest contents to our modified contents.
PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory();
for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) {
onlineEnderChest.setItem(i, this.items.get(i));
}
// Set our item array to the new inventory's array.
this.items = onlineEnderChest.items;
// Add viewers to new inventory.
onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction);
this.playerOnline = true;
}
@Override
public @NotNull org.bukkit.entity.Player getPlayer() {
return owner.getBukkitEntity();
}
@Override
public void setChanged() {
this.owner.getEnderChestInventory().setChanged();
}
@Override
public List<ItemStack> getContents() {
return this.items;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.owner.getEnderChestInventory().getViewers();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public void setActiveChest(EnderChestBlockEntity enderChest) {
this.owner.getEnderChestInventory().setActiveChest(enderChest);
}
@Override
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
}
@Override
public int getMaxStackSize() {
return this.owner.getEnderChestInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int i) {
this.owner.getEnderChestInventory().setMaxStackSize(i);
}
@Override
public InventoryHolder getOwner() {
return this.owner.getEnderChestInventory().getOwner();
}
@Override
public @Nullable Location getLocation() {
return null;
}
@Override
public void addListener(ContainerListener listener) {
this.owner.getEnderChestInventory().addListener(listener);
}
@Override
public void removeListener(ContainerListener listener) {
this.owner.getEnderChestInventory().removeListener(listener);
}
@Override
public ItemStack getItem(int i) {
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
}
@Override
public ItemStack removeItem(int i, int j) {
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public ItemStack addItem(ItemStack itemstack) {
ItemStack localItem = itemstack.copy();
this.moveItemToOccupiedSlotsWithSameType(localItem);
if (localItem.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.moveItemToEmptySlots(localItem);
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
}
}
@Override
public boolean canAddItem(ItemStack itemstack) {
for (ItemStack itemstack1 : this.items) {
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
return true;
}
}
return false;
}
private void moveItemToEmptySlots(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (localItem.isEmpty()) {
this.setItem(i, itemstack.copy());
itemstack.setCount(0);
return;
}
}
}
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
this.moveItemsBetweenStacks(itemstack, localItem);
if (itemstack.isEmpty()) {
return;
}
}
}
}
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
if (j > 0) {
itemstack1.grow(j);
itemstack.shrink(j);
this.setChanged();
}
}
@Override
public ItemStack removeItemNoUpdate(int i) {
ItemStack itemstack = this.items.get(i);
if (itemstack.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.items.set(i, ItemStack.EMPTY);
return itemstack;
}
}
@Override
public void setItem(int i, ItemStack itemstack) {
this.items.set(i, itemstack);
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
this.setChanged();
}
@Override
public int getContainerSize() {
return this.owner.getEnderChestInventory().getContainerSize();
}
@Override
public boolean isEmpty() {
return this.items.stream().allMatch(ItemStack::isEmpty);
}
@Override
public void startOpen(Player player) {
}
@Override
public void stopOpen(Player player) {
}
@Override
public boolean canPlaceItem(int i, ItemStack itemstack) {
return true;
}
@Override
public void clearContent() {
this.items.clear();
this.setChanged();
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountStack(itemstack);
}
}
@Override
public List<ItemStack> removeAllItems() {
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
this.clearContent();
return list;
}
@Override
public ItemStack removeItemType(Item item, int i) {
ItemStack itemstack = new ItemStack(item, 0);
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
ItemStack localItem = this.getItem(j);
if (localItem.getItem().equals(item)) {
int k = i - itemstack.getCount();
ItemStack splitItem = localItem.split(k);
itemstack.grow(splitItem.getCount());
if (itemstack.getCount() == i) {
break;
}
}
}
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public String toString() {
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString();
}
@Override
public void fromTag(ListTag listTag) {
for (int i = 0; i < this.getContainerSize(); ++i) {
this.setItem(i, ItemStack.EMPTY);
}
for (int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
if (j < this.getContainerSize()) {
this.setItem(j, ItemStack.of(compoundTag));
}
}
}
}
@@ -1,806 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_19_R3;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.item.ArmorItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
private boolean playerOnline;
private Player player;
private NonNullList<ItemStack> items;
private NonNullList<ItemStack> armor;
private NonNullList<ItemStack> offhand;
private List<NonNullList<ItemStack>> compartments;
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
super(PlayerDataManager.getHandle(bukkitPlayer));
this.inventory = new CraftInventory(this);
this.playerOnline = online;
this.player = super.player;
this.selected = player.getInventory().selected;
this.items = this.player.getInventory().items;
this.armor = this.player.getInventory().armor;
this.offhand = this.player.getInventory().offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
}
@Override
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
if (this.playerOnline) {
return;
}
Player offlinePlayer = this.player;
Player onlinePlayer = PlayerDataManager.getHandle(player);
onlinePlayer.getInventory().transaction.addAll(this.transaction);
// Set owner to new player.
this.player = onlinePlayer;
// Set player's inventory contents to our modified contents.
Inventory onlineInventory = onlinePlayer.getInventory();
for (int i = 0; i < getContainerSize(); ++i) {
onlineInventory.setItem(i, getRawItem(i));
}
onlineInventory.selected = this.selected;
// Set our item arrays to the new inventory's arrays.
this.items = onlineInventory.items;
this.armor = onlineInventory.armor;
this.offhand = onlineInventory.offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
// Add existing viewers to new viewer list.
Inventory offlineInventory = offlinePlayer.getInventory();
// Remove self from listing - player is always a viewer of their own inventory, prevent duplicates.
offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity());
onlineInventory.transaction.addAll(offlineInventory.transaction);
this.playerOnline = true;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return this.inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public @NotNull HumanEntity getPlayer() {
return this.player.getBukkitEntity();
}
private @NotNull ItemStack getRawItem(int i) {
if (i < 0) {
return ItemStack.EMPTY;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
return list.get(i);
}
}
return ItemStack.EMPTY;
}
private void setRawItem(int i, @NotNull ItemStack itemStack) {
if (i < 0) {
return;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
list.set(i, itemStack);
}
}
}
private record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
if (index < items.size()) {
return new IndexedCompartment(items, getReversedItemSlotNum(index));
}
index -= items.size();
if (index < armor.size()) {
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
}
index -= armor.size();
if (index < offhand.size()) {
return new IndexedCompartment(offhand, index);
}
index -= offhand.size();
return new IndexedCompartment(null, index);
}
private int getReversedArmorSlotNum(final int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
private int getReversedItemSlotNum(final int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
private boolean contains(Predicate<ItemStack> predicate) {
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
}
@Override
public List<ItemStack> getArmorContents() {
return this.armor;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.player.getInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.player.getInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.player.getInventory().getViewers();
}
@Override
public InventoryHolder getOwner() {
return this.player.getBukkitEntity();
}
@Override
public int getMaxStackSize() {
return this.player.getInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int size) {
this.player.getInventory().setMaxStackSize(size);
}
@Override
public Location getLocation() {
return this.player.getBukkitEntity().getLocation();
}
@Override
public boolean hasCustomName() {
return false;
}
@Override
public List<ItemStack> getContents() {
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
}
@Override
public ItemStack getSelected() {
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
}
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
@Override
public int canHold(ItemStack itemstack) {
int remains = itemstack.getCount();
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.getRawItem(i);
if (itemstack1.isEmpty()) {
return itemstack.getCount();
}
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.getCount();
}
}
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
}
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
}
@Override
public int getFreeSlot() {
for(int i = 0; i < this.items.size(); ++i) {
if (this.items.get(i).isEmpty()) {
return i;
}
}
return -1;
}
@Override
public void setPickedItem(ItemStack itemstack) {
int i = this.findSlotMatchingItem(itemstack);
if (isHotbarSlot(i)) {
this.selected = i;
} else if (i == -1) {
this.selected = this.getSuitableHotbarSlot();
if (!this.items.get(this.selected).isEmpty()) {
int j = this.getFreeSlot();
if (j != -1) {
this.items.set(j, this.items.get(this.selected));
}
}
this.items.set(this.selected, itemstack);
} else {
this.pickSlot(i);
}
}
@Override
public void pickSlot(int i) {
this.selected = this.getSuitableHotbarSlot();
ItemStack itemstack = this.items.get(this.selected);
this.items.set(this.selected, this.items.get(i));
this.items.set(i, itemstack);
}
@Override
public int findSlotMatchingItem(ItemStack itemstack) {
for(int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) {
return i;
}
}
return -1;
}
@Override
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
for(int i = 0; i < this.items.size(); ++i) {
ItemStack localItem = this.items.get(i);
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) {
return i;
}
}
return -1;
}
@Override
public int getSuitableHotbarSlot() {
int i;
int j;
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (this.items.get(i).isEmpty()) {
return i;
}
}
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (!this.items.get(i).isEnchanted()) {
return i;
}
}
return this.selected;
}
@Override
public void swapPaint(double d0) {
if (d0 > 0.0D) {
d0 = 1.0D;
}
if (d0 < 0.0D) {
d0 = -1.0D;
}
this.selected = (int) (this.selected - d0);
while (this.selected < 0) {
this.selected += 9;
}
while(this.selected >= 9) {
this.selected -= 9;
}
}
@Override
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
byte b0 = 0;
boolean flag = i == 0;
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
ItemStack itemstack = this.player.containerMenu.getCarried();
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
if (itemstack.isEmpty()) {
this.player.containerMenu.setCarried(ItemStack.EMPTY);
}
return j;
}
private int addResource(ItemStack itemstack) {
int i = this.getSlotWithRemainingSpace(itemstack);
if (i == -1) {
i = this.getFreeSlot();
}
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
}
private int addResource(int i, ItemStack itemstack) {
Item item = itemstack.getItem();
int j = itemstack.getCount();
ItemStack localItemStack = this.getRawItem(i);
if (localItemStack.isEmpty()) {
localItemStack = new ItemStack(item, 0);
if (itemstack.hasTag()) {
// hasTag ensures tag not null
//noinspection ConstantConditions
localItemStack.setTag(itemstack.getTag().copy());
}
this.setRawItem(i, localItemStack);
}
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
k = this.getMaxStackSize() - localItemStack.getCount();
}
if (k != 0) {
j -= k;
localItemStack.grow(k);
localItemStack.setPopTime(5);
}
return j;
}
@Override
public int getSlotWithRemainingSpace(ItemStack itemstack) {
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
return this.selected;
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
return 40;
} else {
for(int i = 0; i < this.items.size(); ++i) {
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
return i;
}
}
return -1;
}
}
@Override
public void tick() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (!compartment.get(i).isEmpty()) {
compartment.get(i).inventoryTick(this.player.level, this.player, i, this.selected == i);
}
}
}
}
@Override
public boolean add(ItemStack itemStack) {
return this.add(-1, itemStack);
}
@Override
public boolean add(int i, ItemStack itemStack) {
if (itemStack.isEmpty()) {
return false;
} else {
try {
if (itemStack.isDamaged()) {
if (i == -1) {
i = this.getFreeSlot();
}
if (i >= 0) {
this.items.set(i, itemStack.copy());
this.items.get(i).setPopTime(5);
itemStack.setCount(0);
return true;
} else if (this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return false;
}
} else {
int j;
do {
j = itemStack.getCount();
if (i == -1) {
itemStack.setCount(this.addResource(itemStack));
} else {
itemStack.setCount(this.addResource(i, itemStack));
}
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return itemStack.getCount() < j;
}
}
} catch (Throwable var6) {
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
throw new ReportedException(crashReport);
}
}
}
@Override
public void placeItemBackInInventory(ItemStack itemStack) {
this.placeItemBackInInventory(itemStack, true);
}
@Override
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
while(true) {
if (!itemStack.isEmpty()) {
int i = this.getSlotWithRemainingSpace(itemStack);
if (i == -1) {
i = this.getFreeSlot();
}
if (i != -1) {
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
}
continue;
}
this.player.drop(itemStack, false);
}
return;
}
}
@Override
public ItemStack removeItem(int rawIndex, final int j) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
return ItemStack.EMPTY;
}
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
}
@Override
public void removeItem(ItemStack itemStack) {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (compartment.get(i) == itemStack) {
compartment.set(i, ItemStack.EMPTY);
break;
}
}
}
}
@Override
public ItemStack removeItemNoUpdate(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
if (removed.isEmpty()) {
return ItemStack.EMPTY;
}
return removed;
}
@Override
public void setItem(int rawIndex, final ItemStack itemStack) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
this.player.drop(itemStack, true);
return;
}
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
}
@Override
public float getDestroySpeed(BlockState blockState) {
return this.items.get(this.selected).getDestroySpeed(blockState);
}
@Override
public ListTag save(ListTag listTag) {
for (int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)i);
this.items.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.armor.size(); ++i) {
if (!this.armor.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 100));
this.armor.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.offhand.size(); ++i) {
if (!this.offhand.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 150));
this.offhand.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
return listTag;
}
@Override
public void load(ListTag listTag) {
this.items.clear();
this.armor.clear();
this.offhand.clear();
for(int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
ItemStack itemstack = ItemStack.of(compoundTag);
if (!itemstack.isEmpty()) {
if (j < this.items.size()) {
this.items.set(j, itemstack);
} else if (j >= 100 && j < this.armor.size() + 100) {
this.armor.set(j - 100, itemstack);
} else if (j >= 150 && j < this.offhand.size() + 150) {
this.offhand.set(j - 150, itemstack);
}
}
}
}
@Override
public int getContainerSize() {
return 45;
}
@Override
public boolean isEmpty() {
return !contains(itemStack -> !itemStack.isEmpty());
}
@Override
public ItemStack getItem(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
return indexedCompartment.compartment().get(indexedCompartment.index());
}
@Override
public Component getName() {
return this.player.getName();
}
@Override
public ItemStack getArmor(int index) {
return this.armor.get(index);
}
@Override
public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) {
if (damage > 0.0F) {
damage /= 4.0F;
if (damage < 1.0F) {
damage = 1.0F;
}
for (int index : armorIndices) {
ItemStack itemstack = this.armor.get(index);
if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
}
}
}
}
@Override
public void dropAll() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
ItemStack itemstack = compartment.get(i);
if (!itemstack.isEmpty()) {
this.player.drop(itemstack, true, false);
compartment.set(i, ItemStack.EMPTY);
}
}
}
}
@Override
public void setChanged() {
super.setChanged();
}
@Override
public int getTimesChanged() {
return super.getTimesChanged();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public boolean contains(ItemStack itemstack) {
return contains(itemStack -> itemStack.isEmpty() && itemStack.sameItem(itemstack));
}
@Override
public boolean contains(TagKey<Item> tagKey) {
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey));
}
@Override
public void replaceWith(Inventory inventory) {
Function<Integer, ItemStack> getter;
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
getter = specialPlayerInventory::getRawItem;
} else {
getter = inventory::getItem;
}
for(int i = 0; i < this.getContainerSize(); ++i) {
this.setRawItem(i, getter.apply(i));
}
this.selected = inventory.selected;
}
@Override
public void clearContent() {
for (NonNullList<ItemStack> compartment : this.compartments) {
compartment.clear();
}
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountSimpleStack(itemstack);
}
}
@Override
public ItemStack removeFromSelected(boolean dropWholeStack) {
ItemStack itemstack = this.getSelected();
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
}
}
-81
View File
@@ -1,81 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2011-2023 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<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.4.2</version>
</parent>
<artifactId>openinvadapter1_20_R2</artifactId>
<name>OpenInvAdapter1_20_R2</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spigot.version>1.20.2-R0.1-SNAPSHOT</spigot.version>
</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>
<scope>provided</scope>
</dependency>
<dependency>
<artifactId>openinvplugincore</artifactId>
<groupId>com.lishid</groupId>
</dependency>
<dependency>
<artifactId>annotations</artifactId>
<groupId>org.jetbrains</groupId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -1,243 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R2;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.util.ReflectionHelper;
import java.lang.reflect.Field;
import java.util.logging.Logger;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.SimpleMenuProvider;
import net.minecraft.world.entity.monster.Shulker;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
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.ShulkerBoxBlock;
import net.minecraft.world.level.block.TrappedChestBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.ShulkerBox;
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class AnySilentContainer implements IAnySilentContainer {
private @Nullable Field serverPlayerGameModeGameType;
public AnySilentContainer() {
try {
try {
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
this.serverPlayerGameModeGameType.setAccessible(true);
} catch (NoSuchFieldException e) {
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!");
logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues");
logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail.");
// 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(java.util.logging.Level.WARNING, "Error obtaining GameType field", e);
}
}
@Override
public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) {
org.bukkit.World bukkitWorld = shulkerBox.getWorld();
if (!(bukkitWorld instanceof CraftWorld)) {
bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID());
}
if (!(bukkitWorld instanceof CraftWorld craftWorld)) {
Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!");
OpenInv.getPlugin(OpenInv.class).getLogger().log(java.util.logging.Level.WARNING, exception.getMessage(), exception);
return false;
}
final ServerLevel world = craftWorld.getHandle();
final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ());
final BlockEntity tile = world.getBlockEntity(blockPosition);
if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity)
|| shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) {
return false;
}
BlockState blockState = world.getBlockState(blockPosition);
// See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen
AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F)
.move(blockPosition)
.deflate(1.0E-6D);
return !world.noCollision(boundingBox);
}
@Override
public boolean activateContainer(
@NotNull final Player bukkitPlayer,
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 net.minecraft.world.level.Level level = player.level();
final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
final BlockEntity blockEntity = level.getBlockEntity(blockPos);
if (blockEntity == null) {
return false;
}
if (blockEntity instanceof EnderChestBlockEntity enderChestTile) {
// Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock
PlayerEnderChestContainer enderChest = player.getEnderChestInventory();
enderChest.setActiveChest(enderChestTile);
player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> {
MenuType<?> containers = PlayerDataManager.getContainers(enderChest.getContainerSize());
int rows = enderChest.getContainerSize() / 9;
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
}, Component.translatable(("container.enderchest"))));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
if (!(blockEntity instanceof MenuProvider menuProvider)) {
return false;
}
BlockState blockState = level.getBlockState(blockPos);
Block block = blockState.getBlock();
if (block instanceof ChestBlock chestBlock) {
// boolean flag: do not check if chest is blocked
menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true);
if (menuProvider == null) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
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 || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) {
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);
// ServerPlayer#closeContainer cannot be called without entering an
// infinite loop because this method is called during inventory close.
// From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent
player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity());
// From ServerPlayer#closeContainer
player.doCloseContainer();
// Regular inventory close will handle the rest - packet sending, etc.
// 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();
}
}
}
@@ -1,139 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R2;
import com.mojang.logging.LogUtils;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.storage.PlayerDataStorage;
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
public class OpenPlayer extends CraftPlayer {
public OpenPlayer(CraftServer server, ServerPlayer entity) {
super(server, entity);
}
@Override
public void loadData() {
PlayerDataManager.loadData(getHandle());
}
@Override
public void saveData() {
ServerPlayer player = this.getHandle();
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
try {
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
setExtraData(playerData);
if (!isOnline()) {
// Preserve certain data when offline.
CompoundTag oldData = worldNBTStorage.load(player);
revertSpecialValues(playerData, oldData);
}
File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
NbtIo.writeCompressed(playerData, file);
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat");
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
Util.safeReplaceFile(file1, file, file2);
} catch (Exception e) {
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
}
}
private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) {
if (oldData == null) {
return;
}
// Prevent vehicle deletion.
if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) {
// See net.minecraft.server.players.PlayerList#save(ServerPlayer)
// See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
try {
Tag attach = oldData.get("Attach");
if (attach != null) {
newData.putUUID("Attach", NbtUtils.loadUUID(attach));
}
} catch (IllegalArgumentException ignored) {
// Likely will not re-mount successfully, but at least the mount will not be deleted.
}
newData.put("Entity", oldData.getCompound("Entity"));
newData.put("RootVehicle", oldData.getCompound("RootVehicle"));
}
// Revert automatic updates to play timestamps.
copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class);
copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class);
copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class);
}
private <T extends Tag> void copyValue(
@NotNull CompoundTag source,
@NotNull CompoundTag target,
@NotNull String container,
@NotNull String key,
@NotNull Class<T> tagType) {
CompoundTag oldContainer = getTag(source, container, CompoundTag.class);
CompoundTag newContainer = getTag(target, container, CompoundTag.class);
// Container being null means the server implementation doesn't store this data.
if (oldContainer == null || newContainer == null) {
return;
}
// If old tag exists, copy it to new location, removing otherwise.
setTag(newContainer, key, getTag(oldContainer, key, tagType));
}
private <T extends Tag> @Nullable T getTag(
@NotNull CompoundTag container,
@NotNull String key,
@NotNull Class<T> dataType) {
Tag value = container.get(key);
if (value == null || !dataType.isAssignableFrom(value.getClass())) {
return null;
}
return dataType.cast(value);
}
private <T extends Tag> void setTag(
@NotNull CompoundTag container,
@NotNull String key,
@Nullable T data) {
if (data == null) {
container.remove(key);
} else {
container.put(key, data);
}
}
}
@@ -1,330 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R2;
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 com.mojang.serialization.Dynamic;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R2.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.UUID;
import java.util.logging.Logger;
public class PlayerDataManager implements IPlayerDataManager {
private static boolean paper;
static {
try {
Class.forName("io.papermc.paper.configuration.Configuration");
paper = true;
} catch (ClassNotFoundException ignored) {
paper = false;
}
}
private @Nullable Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
Logger logger = JavaPlugin.getPlugin(OpenInv.class).getLogger();
logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!");
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
bukkitEntity = null;
}
}
public static @NotNull ServerPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
ServerPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
}
if (nmsPlayer == null) {
// Could use reflection to examine fields, but it's honestly not worth the bother.
throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName());
}
return nmsPlayer;
}
@Override
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
// Ensure player has data
if (!offline.hasPlayedBefore()) {
return null;
}
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
if (worldServer == null) {
return null;
}
// Create a new ServerPlayer.
ServerPlayer entity = createNewPlayer(server, worldServer, offline);
// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
entity.getAdvancements().stopListening();
// Try to load the player's data.
if (loadData(entity)) {
// If data is loaded successfully, return the Bukkit entity.
return entity.getBukkitEntity();
}
return null;
}
private @NotNull ServerPlayer createNewPlayer(
@NotNull MinecraftServer server,
@NotNull ServerLevel worldServer,
@NotNull final OfflinePlayer offline) {
// See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile)
// See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket)
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
ClientInformation dummyInfo = new ClientInformation(
"en_us",
1, // Reduce distance just in case.
ChatVisiblity.HIDDEN, // Don't accept chat.
false,
ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION,
ServerPlayer.DEFAULT_MAIN_HAND,
true,
false // Don't list in player list (not that this player is in the list anyway).
);
ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
}
return entity;
}
static boolean loadData(@NotNull ServerPlayer player) {
// See CraftPlayer#loadData
CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player);
if (loadedData == null) {
// Exceptions with loading are logged by Mojang.
return false;
}
// Read basic data into the player.
player.load(loadedData);
// Also read "extra" data.
player.readAdditionalSaveData(loadedData);
if (paper) {
// Paper: world is not loaded by ServerPlayer#load(CompoundTag).
parseWorld(player, loadedData);
}
return true;
}
private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) {
// See PlayerList#placeNewPlayer
World bukkitWorld;
if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) {
// Modern Bukkit world.
bukkitWorld = org.bukkit.Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast")));
} else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) {
// Legacy Bukkit world.
bukkitWorld = org.bukkit.Bukkit.getServer().getWorld(loadedData.getString("world"));
} else {
// Vanilla player data.
DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension")))
.resultOrPartial(JavaPlugin.getPlugin(OpenInv.class).getLogger()::warning)
.map(player.server::getLevel)
// If ServerLevel exists, set, otherwise move to spawn.
.ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null));
return;
}
if (bukkitWorld == null) {
player.spawnIn(null);
return;
}
player.setServerLevel(((CraftWorld) bukkitWorld).getHandle());
}
private void injectPlayer(ServerPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
}
@Override
public @NotNull Player inject(@NotNull Player player) {
try {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
return openPlayer;
}
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
return player;
}
}
@Override
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.connection == null) {
return null;
}
InventoryView view = getView(player, inventory);
if (view == null) {
return player.openInventory(inventory.getBukkitInventory());
}
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public MenuType<?> getType() {
return getContainers(inventory.getBukkitInventory().getSize());
}
};
container.setTitle(Component.literal(view.getTitle()));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
return null;
}
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
Component.literal(container.getBukkitView().getTitle())));
nmsPlayer.containerMenu = container;
nmsPlayer.initMenu(container);
return container.getBukkitView();
}
private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) {
if (inventory instanceof SpecialEnderChest) {
return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest");
} else if (inventory instanceof SpecialPlayerInventory) {
return new OpenInventoryView(player, inventory, "container.player", "'s Inventory");
} else {
return null;
}
}
static @NotNull MenuType<?> getContainers(int inventorySize) {
return switch (inventorySize) {
case 9 -> MenuType.GENERIC_9x1;
case 18 -> MenuType.GENERIC_9x2;
case 36 -> MenuType.GENERIC_9x4; // PLAYER
case 41, 45 -> MenuType.GENERIC_9x5;
case 54 -> MenuType.GENERIC_9x6;
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
};
}
@Override
public int convertToPlayerSlot(InventoryView view, int rawSlot) {
int topSize = view.getTopInventory().getSize();
if (topSize <= rawSlot) {
// Slot is not inside special inventory, use Bukkit logic.
return view.convertSlot(rawSlot);
}
// Main inventory, slots 0-26 -> 9-35
if (rawSlot < 27) {
return rawSlot + 9;
}
// Hotbar, slots 27-35 -> 0-8
if (rawSlot < 36) {
return rawSlot - 27;
}
// Armor, slots 36-39 -> 39-36
if (rawSlot < 40) {
return 36 + (39 - rawSlot);
}
// Off hand
if (rawSlot == 40) {
return 40;
}
// Drop slots, "out of inventory"
return -1;
}
}
@@ -1,350 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R2;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.ContainerListener;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
private ServerPlayer owner;
private NonNullList<ItemStack> items;
private boolean playerOnline;
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
super(PlayerDataManager.getHandle(player));
this.inventory = new CraftInventory(this);
this.owner = PlayerDataManager.getHandle(player);
this.playerOnline = online;
this.items = this.owner.getEnderChestInventory().items;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
if (this.playerOnline) {
return;
}
ServerPlayer offlinePlayer = this.owner;
ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player);
// Set owner to new player.
this.owner = onlinePlayer;
// Set player's ender chest contents to our modified contents.
PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory();
for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) {
onlineEnderChest.setItem(i, this.items.get(i));
}
// Set our item array to the new inventory's array.
this.items = onlineEnderChest.items;
// Add viewers to new inventory.
onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction);
this.playerOnline = true;
}
@Override
public @NotNull org.bukkit.entity.Player getPlayer() {
return owner.getBukkitEntity();
}
@Override
public void setChanged() {
this.owner.getEnderChestInventory().setChanged();
}
@Override
public List<ItemStack> getContents() {
return this.items;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.owner.getEnderChestInventory().getViewers();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public void setActiveChest(EnderChestBlockEntity enderChest) {
this.owner.getEnderChestInventory().setActiveChest(enderChest);
}
@Override
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
}
@Override
public int getMaxStackSize() {
return this.owner.getEnderChestInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int i) {
this.owner.getEnderChestInventory().setMaxStackSize(i);
}
@Override
public InventoryHolder getOwner() {
return this.owner.getEnderChestInventory().getOwner();
}
@Override
public @Nullable Location getLocation() {
return null;
}
@Override
public void addListener(ContainerListener listener) {
this.owner.getEnderChestInventory().addListener(listener);
}
@Override
public void removeListener(ContainerListener listener) {
this.owner.getEnderChestInventory().removeListener(listener);
}
@Override
public ItemStack getItem(int i) {
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
}
@Override
public ItemStack removeItem(int i, int j) {
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public ItemStack addItem(ItemStack itemstack) {
ItemStack localItem = itemstack.copy();
this.moveItemToOccupiedSlotsWithSameType(localItem);
if (localItem.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.moveItemToEmptySlots(localItem);
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
}
}
@Override
public boolean canAddItem(ItemStack itemstack) {
for (ItemStack itemstack1 : this.items) {
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
return true;
}
}
return false;
}
private void moveItemToEmptySlots(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (localItem.isEmpty()) {
this.setItem(i, itemstack.copy());
itemstack.setCount(0);
return;
}
}
}
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
this.moveItemsBetweenStacks(itemstack, localItem);
if (itemstack.isEmpty()) {
return;
}
}
}
}
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
if (j > 0) {
itemstack1.grow(j);
itemstack.shrink(j);
this.setChanged();
}
}
@Override
public ItemStack removeItemNoUpdate(int i) {
ItemStack itemstack = this.items.get(i);
if (itemstack.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.items.set(i, ItemStack.EMPTY);
return itemstack;
}
}
@Override
public void setItem(int i, ItemStack itemstack) {
this.items.set(i, itemstack);
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
this.setChanged();
}
@Override
public int getContainerSize() {
return this.owner.getEnderChestInventory().getContainerSize();
}
@Override
public boolean isEmpty() {
return this.items.stream().allMatch(ItemStack::isEmpty);
}
@Override
public void startOpen(Player player) {
}
@Override
public void stopOpen(Player player) {
}
@Override
public boolean canPlaceItem(int i, ItemStack itemstack) {
return true;
}
@Override
public void clearContent() {
this.items.clear();
this.setChanged();
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountStack(itemstack);
}
}
@Override
public List<ItemStack> removeAllItems() {
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
this.clearContent();
return list;
}
@Override
public ItemStack removeItemType(Item item, int i) {
ItemStack itemstack = new ItemStack(item, 0);
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
ItemStack localItem = this.getItem(j);
if (localItem.getItem().equals(item)) {
int k = i - itemstack.getCount();
ItemStack splitItem = localItem.split(k);
itemstack.grow(splitItem.getCount());
if (itemstack.getCount() == i) {
break;
}
}
}
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public String toString() {
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString();
}
@Override
public void fromTag(ListTag listTag) {
for (int i = 0; i < this.getContainerSize(); ++i) {
this.setItem(i, ItemStack.EMPTY);
}
for (int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
if (j < this.getContainerSize()) {
this.setItem(j, ItemStack.of(compoundTag));
}
}
}
}
@@ -1,806 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R2;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.item.ArmorItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
private boolean playerOnline;
private Player player;
private NonNullList<ItemStack> items;
private NonNullList<ItemStack> armor;
private NonNullList<ItemStack> offhand;
private List<NonNullList<ItemStack>> compartments;
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
super(PlayerDataManager.getHandle(bukkitPlayer));
this.inventory = new CraftInventory(this);
this.playerOnline = online;
this.player = super.player;
this.selected = player.getInventory().selected;
this.items = this.player.getInventory().items;
this.armor = this.player.getInventory().armor;
this.offhand = this.player.getInventory().offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
}
@Override
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
if (this.playerOnline) {
return;
}
Player offlinePlayer = this.player;
Player onlinePlayer = PlayerDataManager.getHandle(player);
onlinePlayer.getInventory().transaction.addAll(this.transaction);
// Set owner to new player.
this.player = onlinePlayer;
// Set player's inventory contents to our modified contents.
Inventory onlineInventory = onlinePlayer.getInventory();
for (int i = 0; i < getContainerSize(); ++i) {
onlineInventory.setItem(i, getRawItem(i));
}
onlineInventory.selected = this.selected;
// Set our item arrays to the new inventory's arrays.
this.items = onlineInventory.items;
this.armor = onlineInventory.armor;
this.offhand = onlineInventory.offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
// Add existing viewers to new viewer list.
Inventory offlineInventory = offlinePlayer.getInventory();
// Remove self from listing - player is always a viewer of their own inventory, prevent duplicates.
offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity());
onlineInventory.transaction.addAll(offlineInventory.transaction);
this.playerOnline = true;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return this.inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public @NotNull HumanEntity getPlayer() {
return this.player.getBukkitEntity();
}
private @NotNull ItemStack getRawItem(int i) {
if (i < 0) {
return ItemStack.EMPTY;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
return list.get(i);
}
}
return ItemStack.EMPTY;
}
private void setRawItem(int i, @NotNull ItemStack itemStack) {
if (i < 0) {
return;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
list.set(i, itemStack);
}
}
}
private record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
if (index < items.size()) {
return new IndexedCompartment(items, getReversedItemSlotNum(index));
}
index -= items.size();
if (index < armor.size()) {
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
}
index -= armor.size();
if (index < offhand.size()) {
return new IndexedCompartment(offhand, index);
}
index -= offhand.size();
return new IndexedCompartment(null, index);
}
private int getReversedArmorSlotNum(final int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
private int getReversedItemSlotNum(final int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
private boolean contains(Predicate<ItemStack> predicate) {
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
}
@Override
public List<ItemStack> getArmorContents() {
return this.armor;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.player.getInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.player.getInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.player.getInventory().getViewers();
}
@Override
public InventoryHolder getOwner() {
return this.player.getBukkitEntity();
}
@Override
public int getMaxStackSize() {
return this.player.getInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int size) {
this.player.getInventory().setMaxStackSize(size);
}
@Override
public Location getLocation() {
return this.player.getBukkitEntity().getLocation();
}
@Override
public boolean hasCustomName() {
return false;
}
@Override
public List<ItemStack> getContents() {
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
}
@Override
public ItemStack getSelected() {
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
}
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
@Override
public int canHold(ItemStack itemstack) {
int remains = itemstack.getCount();
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.getRawItem(i);
if (itemstack1.isEmpty()) {
return itemstack.getCount();
}
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.getCount();
}
}
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
}
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
}
@Override
public int getFreeSlot() {
for(int i = 0; i < this.items.size(); ++i) {
if (this.items.get(i).isEmpty()) {
return i;
}
}
return -1;
}
@Override
public void setPickedItem(ItemStack itemstack) {
int i = this.findSlotMatchingItem(itemstack);
if (isHotbarSlot(i)) {
this.selected = i;
} else if (i == -1) {
this.selected = this.getSuitableHotbarSlot();
if (!this.items.get(this.selected).isEmpty()) {
int j = this.getFreeSlot();
if (j != -1) {
this.items.set(j, this.items.get(this.selected));
}
}
this.items.set(this.selected, itemstack);
} else {
this.pickSlot(i);
}
}
@Override
public void pickSlot(int i) {
this.selected = this.getSuitableHotbarSlot();
ItemStack itemstack = this.items.get(this.selected);
this.items.set(this.selected, this.items.get(i));
this.items.set(i, itemstack);
}
@Override
public int findSlotMatchingItem(ItemStack itemstack) {
for(int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) {
return i;
}
}
return -1;
}
@Override
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
for(int i = 0; i < this.items.size(); ++i) {
ItemStack localItem = this.items.get(i);
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) {
return i;
}
}
return -1;
}
@Override
public int getSuitableHotbarSlot() {
int i;
int j;
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (this.items.get(i).isEmpty()) {
return i;
}
}
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (!this.items.get(i).isEnchanted()) {
return i;
}
}
return this.selected;
}
@Override
public void swapPaint(double d0) {
if (d0 > 0.0D) {
d0 = 1.0D;
}
if (d0 < 0.0D) {
d0 = -1.0D;
}
this.selected = (int) (this.selected - d0);
while (this.selected < 0) {
this.selected += 9;
}
while(this.selected >= 9) {
this.selected -= 9;
}
}
@Override
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
byte b0 = 0;
boolean flag = i == 0;
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
ItemStack itemstack = this.player.containerMenu.getCarried();
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
if (itemstack.isEmpty()) {
this.player.containerMenu.setCarried(ItemStack.EMPTY);
}
return j;
}
private int addResource(ItemStack itemstack) {
int i = this.getSlotWithRemainingSpace(itemstack);
if (i == -1) {
i = this.getFreeSlot();
}
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
}
private int addResource(int i, ItemStack itemstack) {
Item item = itemstack.getItem();
int j = itemstack.getCount();
ItemStack localItemStack = this.getRawItem(i);
if (localItemStack.isEmpty()) {
localItemStack = new ItemStack(item, 0);
if (itemstack.hasTag()) {
// hasTag ensures tag not null
//noinspection ConstantConditions
localItemStack.setTag(itemstack.getTag().copy());
}
this.setRawItem(i, localItemStack);
}
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
k = this.getMaxStackSize() - localItemStack.getCount();
}
if (k != 0) {
j -= k;
localItemStack.grow(k);
localItemStack.setPopTime(5);
}
return j;
}
@Override
public int getSlotWithRemainingSpace(ItemStack itemstack) {
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
return this.selected;
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
return 40;
} else {
for(int i = 0; i < this.items.size(); ++i) {
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
return i;
}
}
return -1;
}
}
@Override
public void tick() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (!compartment.get(i).isEmpty()) {
compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i);
}
}
}
}
@Override
public boolean add(ItemStack itemStack) {
return this.add(-1, itemStack);
}
@Override
public boolean add(int i, ItemStack itemStack) {
if (itemStack.isEmpty()) {
return false;
} else {
try {
if (itemStack.isDamaged()) {
if (i == -1) {
i = this.getFreeSlot();
}
if (i >= 0) {
this.items.set(i, itemStack.copy());
this.items.get(i).setPopTime(5);
itemStack.setCount(0);
return true;
} else if (this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return false;
}
} else {
int j;
do {
j = itemStack.getCount();
if (i == -1) {
itemStack.setCount(this.addResource(itemStack));
} else {
itemStack.setCount(this.addResource(i, itemStack));
}
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return itemStack.getCount() < j;
}
}
} catch (Throwable var6) {
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
throw new ReportedException(crashReport);
}
}
}
@Override
public void placeItemBackInInventory(ItemStack itemStack) {
this.placeItemBackInInventory(itemStack, true);
}
@Override
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
while(true) {
if (!itemStack.isEmpty()) {
int i = this.getSlotWithRemainingSpace(itemStack);
if (i == -1) {
i = this.getFreeSlot();
}
if (i != -1) {
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
}
continue;
}
this.player.drop(itemStack, false);
}
return;
}
}
@Override
public ItemStack removeItem(int rawIndex, final int j) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
return ItemStack.EMPTY;
}
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
}
@Override
public void removeItem(ItemStack itemStack) {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (compartment.get(i) == itemStack) {
compartment.set(i, ItemStack.EMPTY);
break;
}
}
}
}
@Override
public ItemStack removeItemNoUpdate(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
if (removed.isEmpty()) {
return ItemStack.EMPTY;
}
return removed;
}
@Override
public void setItem(int rawIndex, final ItemStack itemStack) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
this.player.drop(itemStack, true);
return;
}
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
}
@Override
public float getDestroySpeed(BlockState blockState) {
return this.items.get(this.selected).getDestroySpeed(blockState);
}
@Override
public ListTag save(ListTag listTag) {
for (int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)i);
this.items.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.armor.size(); ++i) {
if (!this.armor.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 100));
this.armor.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.offhand.size(); ++i) {
if (!this.offhand.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 150));
this.offhand.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
return listTag;
}
@Override
public void load(ListTag listTag) {
this.items.clear();
this.armor.clear();
this.offhand.clear();
for(int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
ItemStack itemstack = ItemStack.of(compoundTag);
if (!itemstack.isEmpty()) {
if (j < this.items.size()) {
this.items.set(j, itemstack);
} else if (j >= 100 && j < this.armor.size() + 100) {
this.armor.set(j - 100, itemstack);
} else if (j >= 150 && j < this.offhand.size() + 150) {
this.offhand.set(j - 150, itemstack);
}
}
}
}
@Override
public int getContainerSize() {
return 45;
}
@Override
public boolean isEmpty() {
return !contains(itemStack -> !itemStack.isEmpty());
}
@Override
public ItemStack getItem(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
return indexedCompartment.compartment().get(indexedCompartment.index());
}
@Override
public Component getName() {
return this.player.getName();
}
@Override
public ItemStack getArmor(int index) {
return this.armor.get(index);
}
@Override
public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) {
if (damage > 0.0F) {
damage /= 4.0F;
if (damage < 1.0F) {
damage = 1.0F;
}
for (int index : armorIndices) {
ItemStack itemstack = this.armor.get(index);
if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
}
}
}
}
@Override
public void dropAll() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
ItemStack itemstack = compartment.get(i);
if (!itemstack.isEmpty()) {
this.player.drop(itemstack, true, false);
compartment.set(i, ItemStack.EMPTY);
}
}
}
}
@Override
public void setChanged() {
super.setChanged();
}
@Override
public int getTimesChanged() {
return super.getTimesChanged();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public boolean contains(ItemStack itemstack) {
return contains(itemStack -> itemStack.isEmpty() && itemStack.is(itemstack.getItem()));
}
@Override
public boolean contains(TagKey<Item> tagKey) {
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey));
}
@Override
public void replaceWith(Inventory inventory) {
Function<Integer, ItemStack> getter;
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
getter = specialPlayerInventory::getRawItem;
} else {
getter = inventory::getItem;
}
for(int i = 0; i < this.getContainerSize(); ++i) {
this.setRawItem(i, getter.apply(i));
}
this.selected = inventory.selected;
}
@Override
public void clearContent() {
for (NonNullList<ItemStack> compartment : this.compartments) {
compartment.clear();
}
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountSimpleStack(itemstack);
}
}
@Override
public ItemStack removeFromSelected(boolean dropWholeStack) {
ItemStack itemstack = this.getSelected();
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
}
}
-81
View File
@@ -1,81 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2011-2023 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<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.4.2</version>
</parent>
<artifactId>openinvadapter1_20_R3</artifactId>
<name>OpenInvAdapter1_20_R3</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spigot.version>1.20.4-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>
<scope>provided</scope>
</dependency>
<dependency>
<artifactId>openinvplugincore</artifactId>
<groupId>com.lishid</groupId>
</dependency>
<dependency>
<artifactId>annotations</artifactId>
<groupId>org.jetbrains</groupId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -1,244 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.util.ReflectionHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.SimpleMenuProvider;
import net.minecraft.world.entity.monster.Shulker;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
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.ShulkerBoxBlock;
import net.minecraft.world.level.block.TrappedChestBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.ShulkerBox;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.logging.Logger;
public class AnySilentContainer implements IAnySilentContainer {
private @Nullable Field serverPlayerGameModeGameType;
public AnySilentContainer() {
try {
try {
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
this.serverPlayerGameModeGameType.setAccessible(true);
} catch (NoSuchFieldException e) {
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!");
logger.warning("Please report this at https://github.com/Jikoo/OpenInv/issues");
logger.warning("Attempting to fall through using reflection. Please verify that SilentContainer does not fail.");
// 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(java.util.logging.Level.WARNING, "Error obtaining GameType field", e);
}
}
@Override
public boolean isShulkerBlocked(@NotNull ShulkerBox shulkerBox) {
org.bukkit.World bukkitWorld = shulkerBox.getWorld();
if (!(bukkitWorld instanceof CraftWorld)) {
bukkitWorld = Bukkit.getWorld(bukkitWorld.getUID());
}
if (!(bukkitWorld instanceof CraftWorld craftWorld)) {
Exception exception = new IllegalStateException("AnySilentContainer access attempted on an unknown world!");
OpenInv.getPlugin(OpenInv.class).getLogger().log(java.util.logging.Level.WARNING, exception.getMessage(), exception);
return false;
}
final ServerLevel world = craftWorld.getHandle();
final BlockPos blockPosition = new BlockPos(shulkerBox.getX(), shulkerBox.getY(), shulkerBox.getZ());
final BlockEntity tile = world.getBlockEntity(blockPosition);
if (!(tile instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity)
|| shulkerBoxBlockEntity.getAnimationStatus() != ShulkerBoxBlockEntity.AnimationStatus.CLOSED) {
return false;
}
BlockState blockState = world.getBlockState(blockPosition);
// See net.minecraft.world.level.block.ShulkerBoxBlock#canOpen
AABB boundingBox = Shulker.getProgressDeltaAabb(blockState.getValue(ShulkerBoxBlock.FACING), 0.0F, 0.5F)
.move(blockPosition)
.deflate(1.0E-6D);
return !world.noCollision(boundingBox);
}
@Override
public boolean activateContainer(
@NotNull final Player bukkitPlayer,
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 net.minecraft.world.level.Level level = player.level();
final BlockPos blockPos = new BlockPos(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
final BlockEntity blockEntity = level.getBlockEntity(blockPos);
if (blockEntity == null) {
return false;
}
if (blockEntity instanceof EnderChestBlockEntity enderChestTile) {
// Anychest ender chest. See net.minecraft.world.level.block.EnderChestBlock
PlayerEnderChestContainer enderChest = player.getEnderChestInventory();
enderChest.setActiveChest(enderChestTile);
player.openMenu(new SimpleMenuProvider((containerCounter, playerInventory, ignored) -> {
MenuType<?> containers = PlayerDataManager.getContainers(enderChest.getContainerSize());
int rows = enderChest.getContainerSize() / 9;
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
}, Component.translatable(("container.enderchest"))));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
if (!(blockEntity instanceof MenuProvider menuProvider)) {
return false;
}
BlockState blockState = level.getBlockState(blockPos);
Block block = blockState.getBlock();
if (block instanceof ChestBlock chestBlock) {
// boolean flag: do not check if chest is blocked
menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true);
if (menuProvider == null) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
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 || bukkitPlayer.getGameMode() == GameMode.SPECTATOR) {
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);
// ServerPlayer#closeContainer cannot be called without entering an
// infinite loop because this method is called during inventory close.
// From ServerPlayer#closeContainer -> CraftEventFactory#handleInventoryCloseEvent
player.containerMenu.transferTo(player.inventoryMenu, player.getBukkitEntity());
// From ServerPlayer#closeContainer
player.doCloseContainer();
// Regular inventory close will handle the rest - packet sending, etc.
// 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();
}
}
}
@@ -1,141 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R3;
import com.mojang.logging.LogUtils;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.storage.PlayerDataStorage;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.nio.file.Files;
import java.nio.file.Path;
public class OpenPlayer extends CraftPlayer {
public OpenPlayer(CraftServer server, ServerPlayer entity) {
super(server, entity);
}
@Override
public void loadData() {
PlayerDataManager.loadData(getHandle());
}
@Override
public void saveData() {
ServerPlayer player = this.getHandle();
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
try {
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
CompoundTag playerData = player.saveWithoutId(new CompoundTag());
setExtraData(playerData);
if (!isOnline()) {
// Preserve certain data when offline.
CompoundTag oldData = worldNBTStorage.load(player);
revertSpecialValues(playerData, oldData);
}
Path playerDataDir = worldNBTStorage.getPlayerDir().toPath();
Path file = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
NbtIo.writeCompressed(playerData, file);
Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
Util.safeReplaceFile(dataFile, file, backupFile);
} catch (Exception e) {
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
}
}
private void revertSpecialValues(@NotNull CompoundTag newData, @Nullable CompoundTag oldData) {
if (oldData == null) {
return;
}
// Prevent vehicle deletion.
if (oldData.contains("RootVehicle", Tag.TAG_COMPOUND)) {
// See net.minecraft.server.players.PlayerList#save(ServerPlayer)
// See net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
try {
Tag attach = oldData.get("Attach");
if (attach != null) {
newData.putUUID("Attach", NbtUtils.loadUUID(attach));
}
} catch (IllegalArgumentException ignored) {
// Likely will not re-mount successfully, but at least the mount will not be deleted.
}
newData.put("Entity", oldData.getCompound("Entity"));
newData.put("RootVehicle", oldData.getCompound("RootVehicle"));
}
// Revert automatic updates to play timestamps.
copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class);
copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class);
copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class);
}
private <T extends Tag> void copyValue(
@NotNull CompoundTag source,
@NotNull CompoundTag target,
@NotNull String container,
@NotNull String key,
@NotNull Class<T> tagType) {
CompoundTag oldContainer = getTag(source, container, CompoundTag.class);
CompoundTag newContainer = getTag(target, container, CompoundTag.class);
// Container being null means the server implementation doesn't store this data.
if (oldContainer == null || newContainer == null) {
return;
}
// If old tag exists, copy it to new location, removing otherwise.
setTag(newContainer, key, getTag(oldContainer, key, tagType));
}
private <T extends Tag> @Nullable T getTag(
@NotNull CompoundTag container,
@NotNull String key,
@NotNull Class<T> dataType) {
Tag value = container.get(key);
if (value == null || !dataType.isAssignableFrom(value.getClass())) {
return null;
}
return dataType.cast(value);
}
private <T extends Tag> void setTag(
@NotNull CompoundTag container,
@NotNull String key,
@Nullable T data) {
if (data == null) {
container.remove(key);
} else {
container.put(key, data);
}
}
}
@@ -1,330 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.OpenInventoryView;
import com.mojang.authlib.GameProfile;
import com.mojang.serialization.Dynamic;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.UUID;
import java.util.logging.Logger;
public class PlayerDataManager implements IPlayerDataManager {
private static boolean paper;
static {
try {
Class.forName("io.papermc.paper.configuration.Configuration");
paper = true;
} catch (ClassNotFoundException ignored) {
paper = false;
}
}
private @Nullable Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
Logger logger = JavaPlugin.getPlugin(OpenInv.class).getLogger();
logger.warning("Unable to obtain field to inject custom save process - certain player data may be lost when saving!");
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
bukkitEntity = null;
}
}
public static @NotNull ServerPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
ServerPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getUniqueId());
}
if (nmsPlayer == null) {
// Could use reflection to examine fields, but it's honestly not worth the bother.
throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName());
}
return nmsPlayer;
}
@Override
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
// Ensure player has data
if (!offline.hasPlayedBefore()) {
return null;
}
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);
if (worldServer == null) {
return null;
}
// Create a new ServerPlayer.
ServerPlayer entity = createNewPlayer(server, worldServer, offline);
// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
entity.getAdvancements().stopListening();
// Try to load the player's data.
if (loadData(entity)) {
// If data is loaded successfully, return the Bukkit entity.
return entity.getBukkitEntity();
}
return null;
}
private @NotNull ServerPlayer createNewPlayer(
@NotNull MinecraftServer server,
@NotNull ServerLevel worldServer,
@NotNull final OfflinePlayer offline) {
// See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile)
// See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket)
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
ClientInformation dummyInfo = new ClientInformation(
"en_us",
1, // Reduce distance just in case.
ChatVisiblity.HIDDEN, // Don't accept chat.
false,
ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION,
ServerPlayer.DEFAULT_MAIN_HAND,
true,
false // Don't list in player list (not that this player is in the list anyway).
);
ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
}
return entity;
}
static boolean loadData(@NotNull ServerPlayer player) {
// See CraftPlayer#loadData
CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player);
if (loadedData == null) {
// Exceptions with loading are logged by Mojang.
return false;
}
// Read basic data into the player.
player.load(loadedData);
// Also read "extra" data.
player.readAdditionalSaveData(loadedData);
if (paper) {
// Paper: world is not loaded by ServerPlayer#load(CompoundTag).
parseWorld(player, loadedData);
}
return true;
}
private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) {
// See PlayerList#placeNewPlayer
World bukkitWorld;
if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) {
// Modern Bukkit world.
bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast")));
} else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) {
// Legacy Bukkit world.
bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world"));
} else {
// Vanilla player data.
DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension")))
.resultOrPartial(JavaPlugin.getPlugin(OpenInv.class).getLogger()::warning)
.map(player.server::getLevel)
// If ServerLevel exists, set, otherwise move to spawn.
.ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null));
return;
}
if (bukkitWorld == null) {
player.spawnIn(null);
return;
}
player.setServerLevel(((CraftWorld) bukkitWorld).getHandle());
}
private void injectPlayer(ServerPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
}
@Override
public @NotNull Player inject(@NotNull Player player) {
try {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
return openPlayer;
}
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
return player;
}
}
@Override
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.connection == null) {
return null;
}
InventoryView view = getView(player, inventory);
if (view == null) {
return player.openInventory(inventory.getBukkitInventory());
}
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public MenuType<?> getType() {
return getContainers(inventory.getBukkitInventory().getSize());
}
};
container.setTitle(Component.literal(view.getTitle()));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
return null;
}
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
Component.literal(container.getBukkitView().getTitle())));
nmsPlayer.containerMenu = container;
nmsPlayer.initMenu(container);
return container.getBukkitView();
}
private @Nullable InventoryView getView(Player player, ISpecialInventory inventory) {
if (inventory instanceof SpecialEnderChest) {
return new OpenInventoryView(player, inventory, "container.enderchest", "'s Ender Chest");
} else if (inventory instanceof SpecialPlayerInventory) {
return new OpenInventoryView(player, inventory, "container.player", "'s Inventory");
} else {
return null;
}
}
static @NotNull MenuType<?> getContainers(int inventorySize) {
return switch (inventorySize) {
case 9 -> MenuType.GENERIC_9x1;
case 18 -> MenuType.GENERIC_9x2;
case 36 -> MenuType.GENERIC_9x4; // PLAYER
case 41, 45 -> MenuType.GENERIC_9x5;
case 54 -> MenuType.GENERIC_9x6;
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
};
}
@Override
public int convertToPlayerSlot(InventoryView view, int rawSlot) {
int topSize = view.getTopInventory().getSize();
if (topSize <= rawSlot) {
// Slot is not inside special inventory, use Bukkit logic.
return view.convertSlot(rawSlot);
}
// Main inventory, slots 0-26 -> 9-35
if (rawSlot < 27) {
return rawSlot + 9;
}
// Hotbar, slots 27-35 -> 0-8
if (rawSlot < 36) {
return rawSlot - 27;
}
// Armor, slots 36-39 -> 39-36
if (rawSlot < 40) {
return 36 + (39 - rawSlot);
}
// Off hand
if (rawSlot == 40) {
return 40;
}
// Drop slots, "out of inventory"
return -1;
}
}
@@ -1,351 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.internal.ISpecialEnderChest;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.ContainerListener;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
private ServerPlayer owner;
private NonNullList<ItemStack> items;
private boolean playerOnline;
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
super(PlayerDataManager.getHandle(player));
this.inventory = new CraftInventory(this);
this.owner = PlayerDataManager.getHandle(player);
this.playerOnline = online;
this.items = this.owner.getEnderChestInventory().items;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
if (this.playerOnline) {
return;
}
ServerPlayer offlinePlayer = this.owner;
ServerPlayer onlinePlayer = PlayerDataManager.getHandle(player);
// Set owner to new player.
this.owner = onlinePlayer;
// Set player's ender chest contents to our modified contents.
PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory();
for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) {
onlineEnderChest.setItem(i, this.items.get(i));
}
// Set our item array to the new inventory's array.
this.items = onlineEnderChest.items;
// Add viewers to new inventory.
onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction);
this.playerOnline = true;
}
@Override
public @NotNull org.bukkit.entity.Player getPlayer() {
return owner.getBukkitEntity();
}
@Override
public void setChanged() {
this.owner.getEnderChestInventory().setChanged();
}
@Override
public List<ItemStack> getContents() {
return this.items;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.owner.getEnderChestInventory().getViewers();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public void setActiveChest(EnderChestBlockEntity enderChest) {
this.owner.getEnderChestInventory().setActiveChest(enderChest);
}
@Override
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
}
@Override
public int getMaxStackSize() {
return this.owner.getEnderChestInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int i) {
this.owner.getEnderChestInventory().setMaxStackSize(i);
}
@Override
public InventoryHolder getOwner() {
return this.owner.getEnderChestInventory().getOwner();
}
@Override
public @Nullable Location getLocation() {
return null;
}
@Override
public void addListener(ContainerListener listener) {
this.owner.getEnderChestInventory().addListener(listener);
}
@Override
public void removeListener(ContainerListener listener) {
this.owner.getEnderChestInventory().removeListener(listener);
}
@Override
public ItemStack getItem(int i) {
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
}
@Override
public ItemStack removeItem(int i, int j) {
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public ItemStack addItem(ItemStack itemstack) {
ItemStack localItem = itemstack.copy();
this.moveItemToOccupiedSlotsWithSameType(localItem);
if (localItem.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.moveItemToEmptySlots(localItem);
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
}
}
@Override
public boolean canAddItem(ItemStack itemstack) {
for (ItemStack itemstack1 : this.items) {
if (itemstack1.isEmpty() || ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize()) {
return true;
}
}
return false;
}
private void moveItemToEmptySlots(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (localItem.isEmpty()) {
this.setItem(i, itemstack.copy());
itemstack.setCount(0);
return;
}
}
}
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
this.moveItemsBetweenStacks(itemstack, localItem);
if (itemstack.isEmpty()) {
return;
}
}
}
}
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
if (j > 0) {
itemstack1.grow(j);
itemstack.shrink(j);
this.setChanged();
}
}
@Override
public ItemStack removeItemNoUpdate(int i) {
ItemStack itemstack = this.items.get(i);
if (itemstack.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.items.set(i, ItemStack.EMPTY);
return itemstack;
}
}
@Override
public void setItem(int i, ItemStack itemstack) {
this.items.set(i, itemstack);
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
this.setChanged();
}
@Override
public int getContainerSize() {
return this.owner.getEnderChestInventory().getContainerSize();
}
@Override
public boolean isEmpty() {
return this.items.stream().allMatch(ItemStack::isEmpty);
}
@Override
public void startOpen(Player player) {
}
@Override
public void stopOpen(Player player) {
}
@Override
public boolean canPlaceItem(int i, ItemStack itemstack) {
return true;
}
@Override
public void clearContent() {
this.items.clear();
this.setChanged();
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountStack(itemstack);
}
}
@Override
public List<ItemStack> removeAllItems() {
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
this.clearContent();
return list;
}
@Override
public ItemStack removeItemType(Item item, int i) {
ItemStack itemstack = new ItemStack(item, 0);
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
ItemStack localItem = this.getItem(j);
if (localItem.getItem().equals(item)) {
int k = i - itemstack.getCount();
ItemStack splitItem = localItem.split(k);
itemstack.grow(splitItem.getCount());
if (itemstack.getCount() == i) {
break;
}
}
}
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public String toString() {
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString();
}
@Override
public void fromTag(ListTag listTag) {
for (int i = 0; i < this.getContainerSize(); ++i) {
this.setItem(i, ItemStack.EMPTY);
}
for (int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
if (j < this.getContainerSize()) {
this.setItem(j, ItemStack.of(compoundTag));
}
}
}
}
@@ -1,807 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R3;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.item.ArmorItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
private boolean playerOnline;
private Player player;
private NonNullList<ItemStack> items;
private NonNullList<ItemStack> armor;
private NonNullList<ItemStack> offhand;
private List<NonNullList<ItemStack>> compartments;
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
super(PlayerDataManager.getHandle(bukkitPlayer));
this.inventory = new CraftInventory(this);
this.playerOnline = online;
this.player = super.player;
this.selected = player.getInventory().selected;
this.items = this.player.getInventory().items;
this.armor = this.player.getInventory().armor;
this.offhand = this.player.getInventory().offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
}
@Override
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
if (this.playerOnline) {
return;
}
Player offlinePlayer = this.player;
Player onlinePlayer = PlayerDataManager.getHandle(player);
onlinePlayer.getInventory().transaction.addAll(this.transaction);
// Set owner to new player.
this.player = onlinePlayer;
// Set player's inventory contents to our modified contents.
Inventory onlineInventory = onlinePlayer.getInventory();
for (int i = 0; i < getContainerSize(); ++i) {
onlineInventory.setItem(i, getRawItem(i));
}
onlineInventory.selected = this.selected;
// Set our item arrays to the new inventory's arrays.
this.items = onlineInventory.items;
this.armor = onlineInventory.armor;
this.offhand = onlineInventory.offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
// Add existing viewers to new viewer list.
Inventory offlineInventory = offlinePlayer.getInventory();
// Remove self from listing - player is always a viewer of their own inventory, prevent duplicates.
offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity());
onlineInventory.transaction.addAll(offlineInventory.transaction);
this.playerOnline = true;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return this.inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public @NotNull HumanEntity getPlayer() {
return this.player.getBukkitEntity();
}
private @NotNull ItemStack getRawItem(int i) {
if (i < 0) {
return ItemStack.EMPTY;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
return list.get(i);
}
}
return ItemStack.EMPTY;
}
private void setRawItem(int i, @NotNull ItemStack itemStack) {
if (i < 0) {
return;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
list.set(i, itemStack);
}
}
}
private record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
if (index < items.size()) {
return new IndexedCompartment(items, getReversedItemSlotNum(index));
}
index -= items.size();
if (index < armor.size()) {
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
}
index -= armor.size();
if (index < offhand.size()) {
return new IndexedCompartment(offhand, index);
}
index -= offhand.size();
return new IndexedCompartment(null, index);
}
private int getReversedArmorSlotNum(final int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
private int getReversedItemSlotNum(final int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
private boolean contains(Predicate<ItemStack> predicate) {
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
}
@Override
public List<ItemStack> getArmorContents() {
return this.armor;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.player.getInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.player.getInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.player.getInventory().getViewers();
}
@Override
public InventoryHolder getOwner() {
return this.player.getBukkitEntity();
}
@Override
public int getMaxStackSize() {
return this.player.getInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int size) {
this.player.getInventory().setMaxStackSize(size);
}
@Override
public Location getLocation() {
return this.player.getBukkitEntity().getLocation();
}
@Override
public boolean hasCustomName() {
return false;
}
@Override
public List<ItemStack> getContents() {
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
}
@Override
public ItemStack getSelected() {
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
}
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
@Override
public int canHold(ItemStack itemstack) {
int remains = itemstack.getCount();
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.getRawItem(i);
if (itemstack1.isEmpty()) {
return itemstack.getCount();
}
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.getCount();
}
}
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
}
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
}
@Override
public int getFreeSlot() {
for(int i = 0; i < this.items.size(); ++i) {
if (this.items.get(i).isEmpty()) {
return i;
}
}
return -1;
}
@Override
public void setPickedItem(ItemStack itemstack) {
int i = this.findSlotMatchingItem(itemstack);
if (isHotbarSlot(i)) {
this.selected = i;
} else if (i == -1) {
this.selected = this.getSuitableHotbarSlot();
if (!this.items.get(this.selected).isEmpty()) {
int j = this.getFreeSlot();
if (j != -1) {
this.items.set(j, this.items.get(this.selected));
}
}
this.items.set(this.selected, itemstack);
} else {
this.pickSlot(i);
}
}
@Override
public void pickSlot(int i) {
this.selected = this.getSuitableHotbarSlot();
ItemStack itemstack = this.items.get(this.selected);
this.items.set(this.selected, this.items.get(i));
this.items.set(i, itemstack);
}
@Override
public int findSlotMatchingItem(ItemStack itemstack) {
for(int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) {
return i;
}
}
return -1;
}
@Override
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
for(int i = 0; i < this.items.size(); ++i) {
ItemStack localItem = this.items.get(i);
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) {
return i;
}
}
return -1;
}
@Override
public int getSuitableHotbarSlot() {
int i;
int j;
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (this.items.get(i).isEmpty()) {
return i;
}
}
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (!this.items.get(i).isEnchanted()) {
return i;
}
}
return this.selected;
}
@Override
public void swapPaint(double d0) {
if (d0 > 0.0D) {
d0 = 1.0D;
}
if (d0 < 0.0D) {
d0 = -1.0D;
}
this.selected = (int) (this.selected - d0);
while (this.selected < 0) {
this.selected += 9;
}
while(this.selected >= 9) {
this.selected -= 9;
}
}
@Override
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
byte b0 = 0;
boolean flag = i == 0;
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
ItemStack itemstack = this.player.containerMenu.getCarried();
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
if (itemstack.isEmpty()) {
this.player.containerMenu.setCarried(ItemStack.EMPTY);
}
return j;
}
private int addResource(ItemStack itemstack) {
int i = this.getSlotWithRemainingSpace(itemstack);
if (i == -1) {
i = this.getFreeSlot();
}
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
}
private int addResource(int i, ItemStack itemstack) {
Item item = itemstack.getItem();
int j = itemstack.getCount();
ItemStack localItemStack = this.getRawItem(i);
if (localItemStack.isEmpty()) {
localItemStack = new ItemStack(item, 0);
if (itemstack.hasTag()) {
// hasTag ensures tag not null
//noinspection ConstantConditions
localItemStack.setTag(itemstack.getTag().copy());
}
this.setRawItem(i, localItemStack);
}
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
k = this.getMaxStackSize() - localItemStack.getCount();
}
if (k != 0) {
j -= k;
localItemStack.grow(k);
localItemStack.setPopTime(5);
}
return j;
}
@Override
public int getSlotWithRemainingSpace(ItemStack itemstack) {
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
return this.selected;
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
return 40;
} else {
for(int i = 0; i < this.items.size(); ++i) {
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
return i;
}
}
return -1;
}
}
@Override
public void tick() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (!compartment.get(i).isEmpty()) {
compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i);
}
}
}
}
@Override
public boolean add(ItemStack itemStack) {
return this.add(-1, itemStack);
}
@Override
public boolean add(int i, ItemStack itemStack) {
if (itemStack.isEmpty()) {
return false;
} else {
try {
if (itemStack.isDamaged()) {
if (i == -1) {
i = this.getFreeSlot();
}
if (i >= 0) {
this.items.set(i, itemStack.copy());
this.items.get(i).setPopTime(5);
itemStack.setCount(0);
return true;
} else if (this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return false;
}
} else {
int j;
do {
j = itemStack.getCount();
if (i == -1) {
itemStack.setCount(this.addResource(itemStack));
} else {
itemStack.setCount(this.addResource(i, itemStack));
}
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return itemStack.getCount() < j;
}
}
} catch (Throwable var6) {
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
throw new ReportedException(crashReport);
}
}
}
@Override
public void placeItemBackInInventory(ItemStack itemStack) {
this.placeItemBackInInventory(itemStack, true);
}
@Override
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
while(true) {
if (!itemStack.isEmpty()) {
int i = this.getSlotWithRemainingSpace(itemStack);
if (i == -1) {
i = this.getFreeSlot();
}
if (i != -1) {
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
}
continue;
}
this.player.drop(itemStack, false);
}
return;
}
}
@Override
public ItemStack removeItem(int rawIndex, final int j) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
return ItemStack.EMPTY;
}
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
}
@Override
public void removeItem(ItemStack itemStack) {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (compartment.get(i) == itemStack) {
compartment.set(i, ItemStack.EMPTY);
break;
}
}
}
}
@Override
public ItemStack removeItemNoUpdate(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
if (removed.isEmpty()) {
return ItemStack.EMPTY;
}
return removed;
}
@Override
public void setItem(int rawIndex, final ItemStack itemStack) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
this.player.drop(itemStack, true);
return;
}
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
}
@Override
public float getDestroySpeed(BlockState blockState) {
return this.items.get(this.selected).getDestroySpeed(blockState);
}
@Override
public ListTag save(ListTag listTag) {
for (int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)i);
this.items.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.armor.size(); ++i) {
if (!this.armor.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 100));
this.armor.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.offhand.size(); ++i) {
if (!this.offhand.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 150));
this.offhand.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
return listTag;
}
@Override
public void load(ListTag listTag) {
this.items.clear();
this.armor.clear();
this.offhand.clear();
for(int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
ItemStack itemstack = ItemStack.of(compoundTag);
if (!itemstack.isEmpty()) {
if (j < this.items.size()) {
this.items.set(j, itemstack);
} else if (j >= 100 && j < this.armor.size() + 100) {
this.armor.set(j - 100, itemstack);
} else if (j >= 150 && j < this.offhand.size() + 150) {
this.offhand.set(j - 150, itemstack);
}
}
}
}
@Override
public int getContainerSize() {
return 45;
}
@Override
public boolean isEmpty() {
return !contains(itemStack -> !itemStack.isEmpty());
}
@Override
public ItemStack getItem(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
return indexedCompartment.compartment().get(indexedCompartment.index());
}
@Override
public Component getName() {
return this.player.getName();
}
@Override
public ItemStack getArmor(int index) {
return this.armor.get(index);
}
@Override
public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) {
if (damage > 0.0F) {
damage /= 4.0F;
if (damage < 1.0F) {
damage = 1.0F;
}
for (int index : armorIndices) {
ItemStack itemstack = this.armor.get(index);
if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
}
}
}
}
@Override
public void dropAll() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
ItemStack itemstack = compartment.get(i);
if (!itemstack.isEmpty()) {
this.player.drop(itemstack, true, false);
compartment.set(i, ItemStack.EMPTY);
}
}
}
}
@Override
public void setChanged() {
super.setChanged();
}
@Override
public int getTimesChanged() {
return super.getTimesChanged();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public boolean contains(ItemStack itemstack) {
return contains(itemStack -> itemStack.isEmpty() && itemStack.is(itemstack.getItem()));
}
@Override
public boolean contains(TagKey<Item> tagKey) {
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey));
}
@Override
public void replaceWith(Inventory inventory) {
Function<Integer, ItemStack> getter;
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
getter = specialPlayerInventory::getRawItem;
} else {
getter = inventory::getItem;
}
for(int i = 0; i < this.getContainerSize(); ++i) {
this.setRawItem(i, getter.apply(i));
}
this.selected = inventory.selected;
}
@Override
public void clearContent() {
for (NonNullList<ItemStack> compartment : this.compartments) {
compartment.clear();
}
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountSimpleStack(itemstack);
}
}
@Override
public ItemStack removeFromSelected(boolean dropWholeStack) {
ItemStack itemstack = this.getSelected();
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
}
}
-63
View File
@@ -1,63 +0,0 @@
<!--
~ Copyright (C) 2011-2022 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<version>4.4.2</version>
</parent>
<artifactId>openinvplugincore</artifactId>
<name>OpenInvPlugin</name>
<dependencies>
<dependency>
<artifactId>openinvapi</artifactId>
<groupId>com.lishid</groupId>
</dependency>
<dependency>
<artifactId>annotations</artifactId>
<groupId>org.jetbrains</groupId>
</dependency>
<dependency>
<artifactId>spigot-api</artifactId>
<groupId>org.spigotmc</groupId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -1,201 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.util.InventoryAccess;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Constructor;
class InternalAccessor {
private final @NotNull Plugin plugin;
private final String version;
private boolean supported = false;
private IPlayerDataManager playerDataManager;
private IAnySilentContainer anySilentContainer;
InternalAccessor(@NotNull Plugin plugin) {
this.plugin = plugin;
String packageName = plugin.getServer().getClass().getPackage().getName();
this.version = packageName.substring(packageName.lastIndexOf('.') + 1);
try {
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialPlayerInventory");
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialEnderChest");
this.playerDataManager = this.createObject(IPlayerDataManager.class, "PlayerDataManager");
this.anySilentContainer = this.createObject(IAnySilentContainer.class, "AnySilentContainer");
this.supported = InventoryAccess.isUsable();
} catch (Exception ignored) {}
}
public String getReleasesLink() {
return switch (version) {
case "1_4_5", "1_4_6", "v1_4_R1", "v1_5_R2", "v1_5_R3", "v1_6_R1", "v1_6_R2", "v1_6_R3",
"v1_7_R1", "v1_7_R2", "v1_7_R3", "v1_7_R4", "v1_8_R1", "v1_8_R2",
"v1_9_R1", "v1_9_R2", "v1_10_R1", "v1_11_R1", "v1_12_R1"
-> "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)";
case "v1_13_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.0.0";
case "v1_13_R2" -> "https://github.com/lishid/OpenInv/releases/tag/4.0.7";
case "v1_14_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.1";
case "v1_16_R1" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.4";
case "v1_8_R3", "v1_15_R1", "v1_16_R2" -> "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
case "v1_16_R3" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8";
case "v1_17_R1", "v1_18_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10";
case "v1_19_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2";
case "v1_18_R2", "v1_19_R2" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0";
case "v1_20_R1" -> "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1";
default -> "https://github.com/Jikoo/OpenInv/releases";
};
}
private @NotNull <T> T createObject(
@NotNull Class<? extends T> assignableClass,
@NotNull String className,
@NotNull Object @NotNull ... params)
throws ClassCastException, ReflectiveOperationException {
// Fetch internal class if it exists.
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + this.version + "." + className);
// Quick return: no parameters, no need to fiddle about finding the correct constructor.
if (params.length == 0) {
return assignableClass.cast(internalClass.getConstructor().newInstance());
}
// Search constructors for one matching the given parameters
nextConstructor: for (Constructor<?> constructor : internalClass.getConstructors()) {
Class<?>[] requiredClasses = constructor.getParameterTypes();
if (requiredClasses.length != params.length) {
continue;
}
for (int i = 0; i < params.length; ++i) {
if (!requiredClasses[i].isAssignableFrom(params[i].getClass())) {
continue nextConstructor;
}
}
return assignableClass.cast(constructor.newInstance(params));
}
StringBuilder builder = new StringBuilder("Found class ").append(internalClass.getName())
.append(" but cannot find any matching constructors for [");
for (Object object : params) {
builder.append(object.getClass().getName()).append(", ");
}
builder.delete(builder.length() - 2, builder.length());
String message = builder.append(']').toString();
this.plugin.getLogger().warning(message);
throw new NoSuchMethodException(message);
}
private @NotNull <T extends ISpecialInventory> T createSpecialInventory(
@NotNull Class<? extends T> assignableClass,
@NotNull String className,
@NotNull Player player,
boolean online) throws InstantiationException {
if (!this.supported) {
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
}
try {
return this.createObject(assignableClass, className, player, online);
} catch (Exception original) {
InstantiationException exception = new InstantiationException(String.format("Unable to create a new %s", className));
exception.initCause(original.fillInStackTrace());
throw exception;
}
}
/**
* Creates an instance of the IAnySilentContainer implementation for the current server version.
*
* @return the IAnySilentContainer
* @throws IllegalStateException if server version is unsupported
*/
public @NotNull IAnySilentContainer getAnySilentContainer() {
if (!this.supported) {
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
}
return this.anySilentContainer;
}
/**
* Creates an instance of the IPlayerDataManager implementation for the current server version.
*
* @return the IPlayerDataManager
* @throws IllegalStateException if server version is unsupported
*/
public @NotNull IPlayerDataManager getPlayerDataManager() {
if (!this.supported) {
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
}
return this.playerDataManager;
}
/**
* Gets the server implementation version.
*
* @return the version
*/
public @NotNull String getVersion() {
return this.version;
}
/**
* Checks if the server implementation is supported.
*
* @return true if initialized for a supported server version
*/
public boolean isSupported() {
return this.supported;
}
/**
* Creates an instance of the ISpecialEnderChest implementation for the given Player, or
* null if the current version is unsupported.
*
* @param player the Player
* @param online true if the Player is online
* @return the ISpecialEnderChest created
* @throws InstantiationException if the ISpecialEnderChest could not be instantiated
*/
public ISpecialEnderChest newSpecialEnderChest(final Player player, final boolean online) throws InstantiationException {
return this.createSpecialInventory(ISpecialEnderChest.class, "SpecialEnderChest", player, online);
}
/**
* Creates an instance of the ISpecialPlayerInventory implementation for the given Player..
*
* @param player the Player
* @param online true if the Player is online
* @return the ISpecialPlayerInventory created
* @throws InstantiationException if the ISpecialPlayerInventory could not be instantiated
*/
public ISpecialPlayerInventory newSpecialPlayerInventory(final Player player, final boolean online) throws InstantiationException {
return this.createSpecialInventory(ISpecialPlayerInventory.class, "SpecialPlayerInventory", player, online);
}
}
@@ -1,211 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import com.lishid.openinv.internal.ISpecialInventory;
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.InventoryHolder;
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
*/
record InventoryListener(OpenInv plugin) implements Listener {
@EventHandler
private void onInventoryClose(@NotNull final InventoryCloseEvent event) {
if (!(event.getPlayer() instanceof Player player)) {
return;
}
InventoryHolder holder = event.getInventory().getHolder();
if (this.plugin.getSilentContainerStatus(player)
&& holder != null
&& this.plugin.getAnySilentContainer().isAnySilentContainer(holder)) {
this.plugin.getAnySilentContainer().deactivateContainer(player);
}
ISpecialInventory specialInventory = InventoryAccess.getEnderChest(event.getInventory());
if (specialInventory != null) {
this.plugin.handleCloseInventory(specialInventory);
} else {
specialInventory = InventoryAccess.getPlayerInventory(event.getInventory());
if (specialInventory != null) {
this.plugin.handleCloseInventory(specialInventory);
}
}
}
@EventHandler(priority = EventPriority.LOWEST)
private 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)
private 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());
}
}
@@ -1,41 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.util.Permissions;
import java.util.Map;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.jetbrains.annotations.NotNull;
record OfflineHandler(
@NotNull BiFunction<Map<UUID, ? extends ISpecialInventory>, UUID, ISpecialInventory> fetch,
@NotNull Consumer<@NotNull ISpecialInventory> handle) {
static final OfflineHandler REMOVE_AND_CLOSE = new OfflineHandler(
Map::remove,
inventory -> OpenInv.ejectViewers(inventory, viewer -> true)
);
static final OfflineHandler REQUIRE_PERMISSIONS = new OfflineHandler(
Map::get,
inventory -> OpenInv.ejectViewers(inventory, viewer -> !Permissions.OPENOFFLINE.hasPermission(viewer))
);
}
@@ -1,686 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.lishid.openinv.commands.ContainerSettingCommand;
import com.lishid.openinv.commands.OpenInvCommand;
import com.lishid.openinv.commands.SearchContainerCommand;
import com.lishid.openinv.commands.SearchEnchantCommand;
import com.lishid.openinv.commands.SearchInvCommand;
import com.lishid.openinv.event.OpenPlayerSaveEvent;
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.util.ConfigUpdater;
import com.lishid.openinv.util.Permissions;
import com.lishid.openinv.util.StringMetric;
import com.lishid.openinv.util.lang.LanguageManager;
import com.lishid.openinv.util.lang.Replacement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.stream.Stream;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Bukkit;
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.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.profile.PlayerProfile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Open other player's inventory
*
* @author lishid
*/
public class OpenInv extends JavaPlugin implements IOpenInv {
private final Cache<String, PlayerProfile> offlineLookUpCache = CacheBuilder.newBuilder().maximumSize(10).build();
private final Map<UUID, ISpecialPlayerInventory> inventories = new ConcurrentHashMap<>();
private final Map<UUID, ISpecialEnderChest> enderChests = new ConcurrentHashMap<>();
private InternalAccessor accessor;
private LanguageManager languageManager;
private boolean isSpigot = false;
private OfflineHandler offlineHandler;
@Override
public void reloadConfig() {
super.reloadConfig();
this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!isSpigot || !this.accessor.isSupported()) {
this.sendVersionError(sender::sendMessage);
return true;
}
return false;
}
@Override
public void onDisable() {
if (this.disableSaving()) {
return;
}
Stream.concat(inventories.values().stream(), enderChests.values().stream())
.map(inventory -> {
// Cheat a bit - rather than stream twice, evict all viewers during remapping.
ejectViewers(inventory, viewer -> true);
if (inventory.getPlayer() instanceof Player player) {
return player;
}
return null;
})
.filter(Objects::nonNull)
.distinct()
.forEach(player -> {
if (!player.isOnline()) {
player = accessor.getPlayerDataManager().inject(player);
}
player.saveData();
});
}
@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");
this.offlineHandler = disableOfflineAccess() ? OfflineHandler.REMOVE_AND_CLOSE : OfflineHandler.REQUIRE_PERMISSIONS;
try {
Class.forName("org.bukkit.entity.Player$Spigot");
isSpigot = true;
} catch (ClassNotFoundException e) {
e.printStackTrace();
isSpigot = false;
}
// Version check
if (isSpigot && 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 InventoryListener(this), this);
// Register commands to their executors
this.setCommandExecutor(new OpenInvCommand(this), "openinv", "openender");
this.setCommandExecutor(new SearchContainerCommand(this), "searchcontainer");
this.setCommandExecutor(new SearchInvCommand(this), "searchinv", "searchender");
this.setCommandExecutor(new SearchEnchantCommand(this), "searchenchant");
this.setCommandExecutor(new ContainerSettingCommand(this), "silentcontainer", "anycontainer");
} else {
this.sendVersionError(this.getLogger()::warning);
}
}
private void setCommandExecutor(@NotNull CommandExecutor executor, String @NotNull ... commands) {
for (String commandName : commands) {
PluginCommand command = this.getCommand(commandName);
if (command != null) {
command.setExecutor(executor);
}
}
}
private void sendVersionError(@NotNull Consumer<String> messageMethod) {
if (!this.accessor.isSupported()) {
messageMethod.accept("Your server version (" + this.accessor.getVersion() + ") is not supported.");
messageMethod.accept("Please download the correct version of OpenInv here: " + this.accessor.getReleasesLink());
}
if (!isSpigot) {
messageMethod.accept("OpenInv requires that you use Spigot or a Spigot fork. Per the 1.14 update thread");
messageMethod.accept("(https://www.spigotmc.org/threads/369724/ \"A Note on CraftBukkit\"), if you are");
messageMethod.accept("encountering an inconsistency with vanilla that prevents you from using Spigot,");
messageMethod.accept("that is considered a Spigot bug and should be reported as such.");
}
}
@Override
public boolean isSupportedVersion() {
return this.accessor != null && this.accessor.isSupported();
}
@Override
public boolean disableSaving() {
return this.getConfig().getBoolean("settings.disable-saving", false);
}
@Override
public boolean disableOfflineAccess() {
return this.getConfig().getBoolean("settings.disable-offline-access", false);
}
@Override
public boolean noArgsOpensSelf() {
return this.getConfig().getBoolean("settings.command.open.no-args-opens-self", false);
}
@Override
public @NotNull IAnySilentContainer getAnySilentContainer() {
return this.accessor.getAnySilentContainer();
}
@Override
public boolean getAnyContainerStatus(@NotNull final OfflinePlayer offline) {
boolean defaultState = false;
if (offline.isOnline()) {
Player onlinePlayer = offline.getPlayer();
if (onlinePlayer != null) {
defaultState = Permissions.ANY_DEFAULT.hasPermission(onlinePlayer);
}
}
return this.getConfig().getBoolean("toggles.any-chest." + offline.getUniqueId(), defaultState);
}
@Override
public void setAnyContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) {
this.getConfig().set("toggles.any-chest." + offline.getUniqueId(), status);
this.saveConfig();
}
@Override
public boolean getSilentContainerStatus(@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." + offline.getUniqueId(), defaultState);
}
@Override
public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) {
this.getConfig().set("toggles.silent-chest." + offline.getUniqueId(), status);
this.saveConfig();
}
@Override
public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online)
throws InstantiationException {
UUID key = player.getUniqueId();
if (this.enderChests.containsKey(key)) {
return this.enderChests.get(key);
}
ISpecialEnderChest inv = this.accessor.newSpecialEnderChest(player, online);
this.enderChests.put(key, inv);
return inv;
}
@Override
public @NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online)
throws InstantiationException {
UUID key = player.getUniqueId();
if (this.inventories.containsKey(key)) {
return this.inventories.get(key);
}
ISpecialPlayerInventory inv = this.accessor.newSpecialPlayerInventory(player, online);
this.inventories.put(key, inv);
return inv;
}
@Override
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
return this.accessor.getPlayerDataManager().openInventory(player, inventory);
}
@Override
public boolean isPlayerLoaded(@NotNull UUID playerUuid) {
return this.inventories.containsKey(playerUuid) || this.enderChests.containsKey(playerUuid);
}
@Override
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
UUID key = offline.getUniqueId();
if (this.inventories.containsKey(key)) {
return (Player) this.inventories.get(key).getPlayer();
}
if (this.enderChests.containsKey(key)) {
return (Player) this.enderChests.get(key).getPlayer();
}
Player player = offline.getPlayer();
if (player != null) {
return player;
}
if (disableOfflineAccess() || !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;
}
return player;
}
@Override
public @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, "Current stack trace", new Throwable("Current stack trace"));
}
OfflinePlayer player;
try {
UUID uuid = UUID.fromString(name);
player = Bukkit.getOfflinePlayer(uuid);
// Ensure player is an existing player.
if (player.hasPlayedBefore() || player.isOnline()) {
return player;
}
// Return null otherwise.
return null;
} catch (IllegalArgumentException ignored) {
// Not a UUID
}
// Exact online match first.
player = Bukkit.getServer().getPlayerExact(name);
if (player != null) {
return player;
}
// Cached offline match.
PlayerProfile cachedResult = offlineLookUpCache.getIfPresent(name);
if (cachedResult != null && cachedResult.getUniqueId() != null) {
player = Bukkit.getOfflinePlayer(cachedResult.getUniqueId());
// Ensure player is an existing player.
if (player.hasPlayedBefore() || player.isOnline()) {
return player;
}
// Return null otherwise.
return null;
}
// Exact offline match second - ensure offline access works when matchable users are online.
player = Bukkit.getServer().getOfflinePlayer(name);
if (player.hasPlayedBefore()) {
offlineLookUpCache.put(name, player.getPlayerProfile());
return player;
}
// Inexact online match.
player = Bukkit.getServer().getPlayer(name);
if (player != null) {
return player;
}
// Finally, inexact offline match.
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;
}
}
if (player != null) {
// If a match was found, store it.
offlineLookUpCache.put(name, player.getPlayerProfile());
return player;
}
// No players have ever joined the server.
return null;
}
@Override
public void unload(@NotNull final OfflinePlayer offline) {
setPlayerOffline(offline, OfflineHandler.REMOVE_AND_CLOSE);
}
/**
* Evict all viewers lacking cross-world permissions when a {@link Player} changes worlds.
*
* @param player the Player
*/
void changeWorld(@NotNull Player player) {
UUID key = player.getUniqueId();
if (this.inventories.containsKey(key)) {
kickCrossWorldViewers(player, this.inventories.get(key));
}
if (this.enderChests.containsKey(key)) {
kickCrossWorldViewers(player, this.enderChests.get(key));
}
}
private void kickCrossWorldViewers(@NotNull Player player, @NotNull ISpecialInventory inventory) {
ejectViewers(
inventory,
viewer ->
!Permissions.CROSSWORLD.hasPermission(viewer)
&& !Objects.equals(viewer.getWorld(), player.getWorld()));
}
/**
* 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) {
return this.accessor.getPlayerDataManager().convertToPlayerSlot(view, rawSlot);
}
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,
Replacement @NotNull ... 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 void sendMessage(@NotNull CommandSender sender, @NotNull String key) {
String message = getLocalizedMessage(sender, key);
if (message != null && !message.isEmpty()) {
sender.sendMessage(message);
}
}
public void sendMessage(@NotNull CommandSender sender, @NotNull String key, Replacement @NotNull... replacements) {
String message = getLocalizedMessage(sender, key, replacements);
if (message != null && !message.isEmpty()) {
sender.sendMessage(message);
}
}
public void sendSystemMessage(@NotNull Player player, @NotNull String key) {
String message = getLocalizedMessage(player, key);
if (message == null) {
return;
}
int newline = message.indexOf('\n');
if (newline != -1) {
// No newlines in action bar chat.
message = message.substring(0, newline);
}
if (message.isEmpty()) {
return;
}
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message));
}
/**
* Method for handling a Player going offline.
*
* @param player the Player
*/
void setPlayerOffline(@NotNull Player player) {
setPlayerOffline(player, offlineHandler);
}
private void setPlayerOffline(@NotNull OfflinePlayer player, @NotNull OfflineHandler handler) {
UUID key = player.getUniqueId();
setPlayerOffline(inventories, key, handler);
setPlayerOffline(enderChests, key, handler);
}
private void setPlayerOffline(
@NotNull Map<UUID, ? extends ISpecialInventory> map,
@NotNull UUID key,
@NotNull OfflineHandler handler) {
ISpecialInventory inventory = handler.fetch().apply(map, key);
if (inventory == null) {
return;
}
inventory.setPlayerOffline();
if (!inventory.isInUse()) {
map.remove(key);
} else {
handler.handle().accept(inventory);
}
}
void handleCloseInventory(@NotNull ISpecialInventory inventory) {
Map<UUID, ? extends ISpecialInventory> map = inventory instanceof ISpecialPlayerInventory ? inventories : enderChests;
UUID key = inventory.getPlayer().getUniqueId();
@Nullable ISpecialInventory loaded = map.get(key);
if (loaded == null) {
// Loaded inventory has already been removed. Removal will handle saving if necessary.
return;
}
// This should only be possible if a plugin is doing funky things with our inventories.
if (loaded != inventory) {
Inventory bukkitInventory = inventory.getBukkitInventory();
// Just in case, respect contents of the inventory that was just used.
loaded.getBukkitInventory().setContents(bukkitInventory.getContents());
// We need to close this inventory to reduce risk of duplication bugs if the user is offline.
// We don't want to risk recursively closing the same inventory repeatedly, so we schedule dumping viewers.
// Worst case we schedule a couple redundant tasks if several people had the inventory open.
if (inventory.isInUse()) {
getServer().getScheduler().runTask(this, () -> ejectViewers(inventory, viewer -> true));
}
}
// Schedule task to check in use status later this tick. Closing user is still in viewer list.
getServer().getScheduler().runTask(this, () -> {
if (loaded.isInUse()) {
return;
}
// Re-fetch from map - prevents duplicate saves on multi-close.
ISpecialInventory current = map.remove(key);
if (disableSaving()
|| current == null
|| !(current.getPlayer() instanceof Player player)
|| player.isOnline()) {
return;
}
OpenPlayerSaveEvent event = new OpenPlayerSaveEvent(player, current);
getServer().getPluginManager().callEvent(event);
if (!event.isCancelled()) {
this.accessor.getPlayerDataManager().inject(player).saveData();
}
});
}
/**
* Method for handling a Player coming online.
*
* @param player the Player
* @throws IllegalStateException if the server version is unsupported
*/
void setPlayerOnline(@NotNull Player player) {
setPlayerOnline(inventories, player, player::updateInventory);
setPlayerOnline(enderChests, player, null);
if (player.hasPlayedBefore()) {
return;
}
// New player may have a name that already points to someone else in lookup cache.
String name = player.getName();
this.offlineLookUpCache.invalidate(name);
// If no offline matches are mapped, don't hit scheduler.
if (this.offlineLookUpCache.size() == 0) {
return;
}
// New player may also be a more exact match than one already in the cache.
// I.e. new player "lava1" is a better match for "lava" than "lava123"
// Player joins are already quite intensive, so this is run on a delay.
this.getServer().getScheduler().runTaskLaterAsynchronously(this, () -> {
Iterator<Map.Entry<String, PlayerProfile>> iterator = this.offlineLookUpCache.asMap().entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, PlayerProfile> entry = iterator.next();
String oldMatch = entry.getValue().getName();
// Shouldn't be possible - all profiles should be complete.
if (oldMatch == null) {
iterator.remove();
continue;
}
String lookup = entry.getKey();
float oldMatchScore = StringMetric.compareJaroWinkler(lookup, oldMatch);
float newMatchScore = StringMetric.compareJaroWinkler(lookup, name);
// If new match exceeds old match, delete old match.
if (newMatchScore > oldMatchScore) {
iterator.remove();
}
}
}, 101L); // Odd delay for pseudo load balancing; Player tasks are usually scheduled with full seconds.
}
private void setPlayerOnline(
@NotNull Map<UUID, ? extends ISpecialInventory> map,
@NotNull Player player,
@Nullable Runnable task) {
ISpecialInventory inventory = map.get(player.getUniqueId());
if (inventory == null) {
// Inventory not open.
return;
}
inventory.setPlayerOnline(player);
// Eject viewers lacking permission.
ejectViewers(
inventory,
viewer ->
!Permissions.OPENONLINE.hasPermission(viewer)
|| !Permissions.CROSSWORLD.hasPermission(viewer)
&& !Objects.equals(viewer.getWorld(), inventory.getPlayer().getWorld()));
if (task != null) {
getServer().getScheduler().runTask(this, task);
}
}
static void ejectViewers(@NotNull ISpecialInventory inventory, @NotNull Predicate<@NotNull HumanEntity> predicate) {
Inventory bukkitInventory = inventory.getBukkitInventory();
for (HumanEntity viewer : new ArrayList<>(bukkitInventory.getViewers())) {
if (viewer.getUniqueId().equals(inventory.getPlayer().getUniqueId())
&& !viewer.getOpenInventory().getTopInventory().equals(bukkitInventory)) {
// Skip owner with other inventory open. They aren't actually a viewer.
continue;
}
if (predicate.test(viewer)) {
viewer.closeInventory();
}
}
}
}
@@ -1,88 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
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;
import org.jetbrains.annotations.NotNull;
record PlayerListener(OpenInv plugin) implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
private void onPlayerJoin(@NotNull PlayerJoinEvent event) {
plugin.setPlayerOnline(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
private void onPlayerQuit(@NotNull PlayerQuitEvent event) {
plugin.setPlayerOffline(event.getPlayer());
}
@EventHandler
private void onWorldChange(@NotNull PlayerChangedWorldEvent event) {
plugin.changeWorld(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
private void onPlayerInteract(@NotNull 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.getAnyContainerStatus(player);
boolean needsAny = plugin.getAnySilentContainer().isAnyContainerNeeded(event.getClickedBlock());
if (!any && needsAny) {
return;
}
boolean silent = Permissions.SILENT.hasPermission(player) && plugin.getSilentContainerStatus(player);
// If anycontainer or silentcontainer is active
if (any || silent) {
if (plugin.getAnySilentContainer().activateContainer(player, silent, event.getClickedBlock())) {
if (silent && needsAny) {
plugin.sendSystemMessage(player, "messages.info.containerBlockedSilent");
} else if (needsAny) {
plugin.sendSystemMessage(player, "messages.info.containerBlocked");
} else if (silent) {
plugin.sendSystemMessage(player, "messages.info.containerSilent");
}
}
event.setCancelled(true);
}
}
}
@@ -1,91 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.TabCompleter;
import com.lishid.openinv.util.lang.Replacement;
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 player)) {
plugin.sendMessage(sender, "messages.error.consoleUnsupported");
return true;
}
boolean any = command.getName().startsWith("any");
Predicate<Player> getSetting = any ? plugin::getAnyContainerStatus : plugin::getSilentContainerStatus;
BiConsumer<OfflinePlayer, Boolean> setSetting = any ? plugin::setAnyContainerStatus : plugin::setSilentContainerStatus;
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",
new Replacement("%setting%", any ? "AnyContainer" : "SilentContainer"),
new Replacement("%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,225 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.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 com.lishid.openinv.util.lang.Replacement;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.StringJoiner;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
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) {
boolean openInv = command.getName().equals("openinv");
if (openInv && args.length > 0 && (args[0].equalsIgnoreCase("help") || args[0].equals("?"))) {
this.showHelp(sender);
return true;
}
if (!(sender instanceof Player player)) {
plugin.sendMessage(sender, "messages.error.consoleUnsupported");
return true;
}
String noArgValue;
if (plugin.noArgsOpensSelf()) {
noArgValue = player.getUniqueId().toString();
} else {
// History management
noArgValue = (openInv ? this.openInvHistory : this.openEnderHistory).get(player);
if (noArgValue == null || noArgValue.isEmpty()) {
noArgValue = player.getUniqueId().toString();
(openInv ? this.openInvHistory : this.openEnderHistory).put(player, noArgValue);
}
}
final String name;
if (args.length < 1) {
name = noArgValue;
} 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 showHelp(final CommandSender sender) {
// Get registered commands
for (String commandName : plugin.getDescription().getCommands().keySet()) {
PluginCommand command = plugin.getCommand(commandName);
// Ensure command is successfully registered and sender can use it
if (command == null || !command.testPermissionSilent(sender)) {
continue;
}
// Send usage
sender.sendMessage(command.getUsage().replace("<command>", commandName));
List<String> aliases = command.getAliases();
if (!aliases.isEmpty()) {
// Assemble alias list
StringJoiner aliasJoiner = new StringJoiner(", ", " (aliases: ", ")");
for (String alias : aliases) {
aliasJoiner.add(alias);
}
// Send all aliases
sender.sendMessage(aliasJoiner.toString());
}
}
}
private void openInventory(final Player player, final OfflinePlayer target, boolean openinv) {
Player onlineTarget;
boolean online = target.isOnline();
if (!online) {
if (!plugin.disableOfflineAccess() && 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",
new Replacement("%target%", onlineTarget.getDisplayName()));
return;
}
// Crossworld check
if (!Permissions.CROSSWORLD.hasPermission(player)
&& !onlineTarget.getWorld().equals(player.getWorld())) {
plugin.sendMessage(
player,
"messages.error.permissionCrossWorld",
new Replacement("%target%", onlineTarget.getDisplayName()));
return;
}
}
if (!plugin.noArgsOpensSelf()) {
// Record the target
(openinv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString());
}
// 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,140 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.TabCompleter;
import com.lishid.openinv.util.lang.Replacement;
import java.util.Collections;
import java.util.List;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockState;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
/**
* Command for searching containers in a radius of chunks.
*/
public class SearchContainerCommand implements TabExecutor {
private final OpenInv plugin;
public SearchContainerCommand(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 senderPlayer)) {
plugin.sendMessage(sender, "messages.error.consoleUnsupported");
return true;
}
if (args.length < 1) {
// Must supply material
return false;
}
Material material = Material.getMaterial(args[0].toUpperCase());
if (material == null) {
plugin.sendMessage(
sender,
"messages.error.invalidMaterial",
new Replacement("%target%", args[0]));
return false;
}
int radius = 5;
if (args.length > 1) {
try {
radius = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
// Invalid radius supplied
return false;
}
}
// Clamp radius.
int configMax = plugin.getConfig().getInt("settings.command.searchcontainer.max-radius", 10);
radius = Math.max(0, Math.min(radius, configMax));
World world = senderPlayer.getWorld();
Chunk centerChunk = senderPlayer.getLocation().getChunk();
StringBuilder locations = new StringBuilder();
for (int dX = -radius; dX <= radius; ++dX) {
for (int dZ = -radius; dZ <= radius; ++dZ) {
if (!world.loadChunk(centerChunk.getX() + dX, centerChunk.getZ() + dZ, false)) {
continue;
}
Chunk chunk = world.getChunkAt(centerChunk.getX() + dX, centerChunk.getZ() + dZ);
for (BlockState tileEntity : chunk.getTileEntities()) {
if (!(tileEntity instanceof InventoryHolder holder)) {
continue;
}
if (!holder.getInventory().contains(material)) {
continue;
}
locations.append(holder.getInventory().getType().name().toLowerCase()).append(" (")
.append(tileEntity.getX()).append(',').append(tileEntity.getY()).append(',')
.append(tileEntity.getZ()).append("), ");
}
}
}
// Matches found, delete trailing comma and space
if (locations.length() > 0) {
locations.delete(locations.length() - 2, locations.length());
} else {
plugin.sendMessage(
sender,
"messages.info.container.noMatches",
new Replacement("%target%", material.name()));
return true;
}
plugin.sendMessage(
sender,
"messages.info.container.matches",
new Replacement("%target%", material.name()),
new Replacement("%detail%", locations.toString()));
return true;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (args.length < 1 || args.length > 2 || !command.testPermissionSilent(sender)) {
return Collections.emptyList();
}
String argument = args[args.length - 1];
if (args.length == 1) {
return TabCompleter.completeEnum(argument, Material.class);
} else {
return TabCompleter.completeInteger(argument);
}
}
}
@@ -1,173 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.TabCompleter;
import com.lishid.openinv.util.lang.Replacement;
import java.util.Collections;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Command adding the ability to search online players' inventories for enchantments of a specific
* type at or above the level specified.
*
* @author Jikoo
*/
public class SearchEnchantCommand implements TabExecutor {
private final OpenInv plugin;
public SearchEnchantCommand(OpenInv plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (args.length == 0) {
return false;
}
Enchantment enchant = null;
int level = 0;
for (String argument : args) {
try {
level = Integer.parseInt(argument);
continue;
} catch (NumberFormatException ignored) {}
argument = argument.toLowerCase();
int colon = argument.indexOf(':');
NamespacedKey key;
try {
if (colon > -1 && colon < argument.length() - 1) {
key = new NamespacedKey(argument.substring(0, colon), argument.substring(colon + 1));
} else {
key = NamespacedKey.minecraft(argument);
}
} catch (IllegalArgumentException ignored) {
continue;
}
Enchantment localEnchant = Enchantment.getByKey(key);
if (localEnchant != null) {
enchant = localEnchant;
}
}
// Arguments not set correctly
if (level == 0 && enchant == null) {
return false;
}
StringBuilder players = new StringBuilder();
for (Player player : plugin.getServer().getOnlinePlayers()) {
boolean flagInventory = containsEnchantment(player.getInventory(), enchant, level);
boolean flagEnder = containsEnchantment(player.getEnderChest(), enchant, level);
// No matches, continue
if (!flagInventory && !flagEnder) {
continue;
}
// Matches, append details
players.append(player.getName()).append(" (");
if (flagInventory) {
players.append("inv");
}
if (flagEnder) {
if (flagInventory) {
players.append(',');
}
players.append("ender");
}
players.append("), ");
}
if (players.length() > 0) {
// Matches found, delete trailing comma and space
players.delete(players.length() - 2, players.length());
} else {
plugin.sendMessage(
sender,
"messages.info.player.noMatches",
new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level));
return true;
}
plugin.sendMessage(
sender,
"messages.info.player.matches",
new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level),
new Replacement("%detail%", players.toString()));
return true;
}
private boolean containsEnchantment(Inventory inventory, @Nullable Enchantment enchant, int minLevel) {
for (ItemStack item : inventory.getContents()) {
if (item == null || item.getType() == Material.AIR) {
continue;
}
if (enchant != null) {
if (item.containsEnchantment(enchant) && item.getEnchantmentLevel(enchant) >= minLevel) {
return true;
}
} else {
if (!item.hasItemMeta()) {
continue;
}
ItemMeta meta = item.getItemMeta();
if (meta == null || !meta.hasEnchants()) {
continue;
}
for (int enchLevel : meta.getEnchants().values()) {
if (enchLevel >= minLevel) {
return true;
}
}
}
}
return false;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!command.testPermissionSilent(sender) || args.length < 1 || args.length > 2) {
return Collections.emptyList();
}
if (args.length == 1) {
return TabCompleter.completeObject(args[0], enchantment -> enchantment.getKey().toString(), Enchantment.values());
} else {
return TabCompleter.completeInteger(args[1]);
}
}
}
@@ -1,113 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.TabCompleter;
import com.lishid.openinv.util.lang.Replacement;
import java.util.Collections;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
public class SearchInvCommand implements TabExecutor {
private final OpenInv plugin;
public SearchInvCommand(OpenInv plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
Material material = null;
if (args.length >= 1) {
material = Material.getMaterial(args[0].toUpperCase());
}
if (material == null) {
plugin.sendMessage(
sender,
"messages.error.invalidMaterial",
new Replacement("%target%", args.length > 0 ? args[0] : "null"));
return false;
}
int count = 1;
if (args.length >= 2) {
try {
count = Integer.parseInt(args[1]);
} catch (NumberFormatException ex) {
plugin.sendMessage(
sender,
"messages.error.invalidNumber",
new Replacement("%target%", args[1]));
return false;
}
}
StringBuilder players = new StringBuilder();
boolean searchInv = command.getName().equals("searchinv");
for (Player player : plugin.getServer().getOnlinePlayers()) {
Inventory inventory = 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 {
plugin.sendMessage(
sender,
"messages.info.player.noMatches",
new Replacement("%target%", material.name()));
return true;
}
plugin.sendMessage(
sender,
"messages.info.player.matches",
new Replacement("%target%", material.name()),
new Replacement("%detail%", players.toString()));
return true;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (args.length < 1 || args.length > 2 || !command.testPermissionSilent(sender)) {
return Collections.emptyList();
}
String argument = args[args.length - 1];
if (args.length == 1) {
return TabCompleter.completeEnum(argument, Material.class);
} else {
return TabCompleter.completeInteger(argument);
}
}
}
@@ -1,66 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import 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);
}
@@ -1,84 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.lang.Replacement;
import java.util.Objects;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
public class OpenInventoryView extends InventoryView {
private final @NotNull Player player;
private final @NotNull ISpecialInventory inventory;
private final @NotNull String titleKey;
private final @NotNull String titleDefaultSuffix;
private String title;
public OpenInventoryView(
@NotNull Player player,
@NotNull ISpecialInventory inventory,
@NotNull String titleKey,
@NotNull 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(
player,
titleKey,
new Replacement("%player%", owner.getName()));
title = Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + titleDefaultSuffix);
}
return title;
}
}
@@ -1,162 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.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 record ConfigUpdater(OpenInv 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();
}
if (version < 6) {
updateConfig5To6();
}
plugin.getServer().getScheduler().runTask(plugin, () -> {
plugin.saveConfig();
plugin.getLogger().info("Configuration update complete!");
});
});
}
private void updateConfig5To6() {
plugin.getServer().getScheduler().runTask(plugin, () -> {
plugin.getConfig().set("settings.command.open.no-args-opens-self", false);
plugin.getConfig().set("settings.command.searchcontainer.max-radius", 10);
plugin.getConfig().set("config-version", 6);
});
}
private void updateConfig4To5() {
plugin.getServer().getScheduler().runTask(plugin, () -> {
plugin.getConfig().set("settings.disable-offline-access", false);
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(player.getUniqueId().toString(), 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,79 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import org.bukkit.permissions.Permissible;
import org.jetbrains.annotations.NotNull;
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"),
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(@NotNull 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("*");
}
}
@@ -1,147 +0,0 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.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.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
/**
* 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(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(String argument, Class<? extends Enum<?>> enumClazz) {
argument = argument.toLowerCase(Locale.ENGLISH);
List<String> completions = new ArrayList<>();
for (Enum<?> enumConstant : enumClazz.getEnumConstants()) {
String name = enumConstant.name().toLowerCase();
if (name.startsWith(argument)) {
completions.add(name);
}
}
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(String argument, String[] 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(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(String argument, Function<T, String> converter, T[] options) {
argument = argument.toLowerCase(Locale.ENGLISH);
List<String> completions = new ArrayList<>();
for (T option : options) {
String optionString = converter.apply(option).toLowerCase();
if (optionString.startsWith(argument)) {
completions.add(optionString);
}
}
return completions;
}
private TabCompleter() {}
}
@@ -1,218 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util.lang;
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.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.logging.Level;
import org.bukkit.ChatColor;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A simple language manager supporting both custom and bundled languages.
*
* @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 @NotNull YamlConfiguration getOrLoadLocale(@NotNull String locale) {
YamlConfiguration loaded = locales.get(locale);
if (loaded != null) {
return loaded;
}
File file = new File(plugin.getDataFolder(), locale + ".yml");
// Load locale config from disk and bundled locale defaults.
YamlConfiguration localeConfig = loadLocale(locale, file);
// If the locale is not the default locale, also handle any missing translations from the default locale.
if (!locale.equals(defaultLocale)) {
addTranslationFallthrough(locale, localeConfig, file);
if (plugin.getConfig().getBoolean("settings.secret.warn-about-guess-section", true)
&& localeConfig.isConfigurationSection("guess")) {
// Warn that guess section exists. This should run once per language per server restart
// when accessed by a user to hint to server owners that they can make UX improvements.
plugin.getLogger().info(() -> "[LanguageManager] Missing translations from " + locale + ".yml! Check the guess section!");
}
}
locales.put(locale, localeConfig);
return localeConfig;
}
private @NotNull YamlConfiguration loadLocale(
@NotNull String locale,
@NotNull File file) {
// Load defaults from the plugin's bundled resources.
InputStream resourceStream = plugin.getResource("locale/" + locale + ".yml");
YamlConfiguration localeConfigDefaults;
if (resourceStream == null) {
localeConfigDefaults = new YamlConfiguration();
} else {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream, StandardCharsets.UTF_8))) {
localeConfigDefaults = YamlConfiguration.loadConfiguration(reader);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + locale + ".yml");
localeConfigDefaults = new YamlConfiguration();
}
}
if (!file.exists()) {
// If the file does not exist on disk, save bundled defaults.
try {
localeConfigDefaults.save(file);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml");
}
// Return loaded bundled locale.
return localeConfigDefaults;
}
// If the file does exist on disk, load it.
YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(file);
// Check for missing translations from the bundled file.
List<String> newKeys = getMissingKeys(localeConfigDefaults, localeConfig::isSet);
if (newKeys.isEmpty()) {
return localeConfig;
}
// Get guess section for missing keys.
ConfigurationSection guess = localeConfig.getConfigurationSection("guess");
for (String newKey : newKeys) {
// Set all missing keys to defaults.
localeConfig.set(newKey, localeConfigDefaults.get(newKey));
// Delete relevant guess keys in case this is a new translation.
if (guess != null) {
guess.set(newKey, null);
}
}
// If guess section is empty, delete it.
if (guess != null && guess.getKeys(false).isEmpty()) {
localeConfig.set("guess", null);
}
plugin.getLogger().info(() -> "[LanguageManager] Added new translation keys to " + locale + ".yml: " + String.join(", ", newKeys));
// Write new keys to disk.
try {
localeConfig.save(file);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml");
}
return localeConfig;
}
private void addTranslationFallthrough(
@NotNull String locale,
@NotNull YamlConfiguration localeConfig,
@NotNull File file) {
YamlConfiguration defaultLocaleConfig = locales.get(defaultLocale);
// Get missing keys. Keys that already have a guess value are not new and don't need to trigger another write.
List<String> missingKeys = getMissingKeys(
defaultLocaleConfig,
key -> localeConfig.isSet(key) || localeConfig.isSet("guess." + key));
if (!missingKeys.isEmpty()) {
// Set up guess section for missing keys.
for (String key : missingKeys) {
localeConfig.set("guess." + key, defaultLocaleConfig.get(key));
}
// Write modified guess section to disk.
try {
localeConfig.save(file);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + locale + ".yml");
}
}
// Fall through to default locale.
localeConfig.setDefaults(defaultLocaleConfig);
}
private @NotNull List<String> getMissingKeys(
@NotNull Configuration configurationDefault,
@NotNull Predicate<String> nodeSetPredicate) {
List<String> missingKeys = new ArrayList<>();
for (String key : configurationDefault.getKeys(true)) {
if (!configurationDefault.isConfigurationSection(key) && !nodeSetPredicate.test(key)) {
// Missing keys are non-section keys that fail the predicate.
missingKeys.add(key);
}
}
return missingKeys;
}
public @Nullable String getValue(@NotNull String key, @Nullable String locale) {
String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase()).getString(key);
if (value == null || value.isEmpty()) {
return null;
}
value = ChatColor.translateAlternateColorCodes('&', value);
return value;
}
public @Nullable String getValue(@NotNull String key, @Nullable String locale, Replacement @NotNull ... replacements) {
String value = getValue(key, locale);
if (value == null) {
return null;
}
for (Replacement replacement : replacements) {
value = value.replace(replacement.placeholder(), replacement.value());
}
return value;
}
}
@@ -1,29 +0,0 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util.lang;
import org.jetbrains.annotations.NotNull;
/**
* A data holder for string replacement in translations.
*
* @param placeholder the placeholder to be replaced
* @param value the value to insert
*/
public record Replacement(@NotNull String placeholder, @NotNull String value) {
}
-10
View File
@@ -1,10 +0,0 @@
config-version: 6
settings:
command:
open:
no-args-opens-self: false
searchcontainer:
max-radius: 10
disable-offline-access: false
disable-saving: false
locale: 'en_us'
@@ -1,30 +0,0 @@
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'
@@ -1,30 +0,0 @@
messages:
error:
consoleUnsupported: 'You cannot use this command from console.'
lootNotGenerated: '&cLoot not generated! Please disable &b/silentcontainer&c.'
invalidMaterial: '&cInvalid material: "%target%"'
invalidNumber: '&cInvalid number: "%target%"'
invalidPlayer: '&cPlayer not found!'
permissionOpenSelf: '&cYou''re not allowed to open your own inventory.'
permissionEnderAll: '&cYou''re not allowed to access other players'' ender chests.'
permissionExempt: '&c%target%''s inventory is protected.'
permissionCrossWorld: '&c%target% is not in your world.'
permissionPlayerOnline: '&cYou''re not allowed to open the inventory of online players.'
permissionPlayerOffline: '&cYou''re not allowed to open the inventory of offline players.'
commandException: '&cAn error occurred. Please check console for details.'
info:
containerBlocked: 'You are opening a blocked container.'
containerBlockedSilent: 'You are opening a blocked container silently.'
containerSilent: 'You are opening a container silently.'
settingState: '%setting%: %state%'
player:
noMatches: 'No players found with %target%.'
matches: 'Players holding %target%: %detail%'
container:
noMatches: 'No containers found with %target%.'
matches: 'Containers holding %target%: %detail%'
on: 'on'
off: 'off'
container:
player: '%player%''s Inventory'
enderchest: '%player%''s Ender Chest'
@@ -1,31 +0,0 @@
messages:
error:
consoleUnsupported: 'No puedes usar este comando desde la consola.'
lootNotGenerated: '&c¡Botín no generado! Desactiva &b/silentcontainer&c.'
invalidMaterial: '&cMaterial no válido: "%target%"'
invalidNumber: '&cNúmero no válido: "%target%"'
invalidPlayer: '&cJugador no encontrado'
permissionOpenSelf: '&cNo tienes permisos para abrir tu inventario de esa forma.'
permissionEnderAll: '&cNo tienes permiso para abrir cofres de ender de otros jugadores.'
permissionExempt: '&c El inventario de %target% está protegido.'
permissionCrossWorld: '&c%target% no está en tu dimensión.'
permissionPlayerOnline: '&cNo estás autorizado para abrir inventarios de jugadores conectados.'
permissionPlayerOffline: '&cNo estás autorizado para abrir inventarios de jugadores desconectados.'
commandException: '&cSe ha producido un error, lee la consola para mas información.'
info:
containerBlocked: 'Estás abriendo un contenedor protegido.'
containerBlockedSilent: 'Estás abriendo sigilosamente un contenedor protegido.'
containerSilent: 'Estás abriendo un contenedor sigilosamente.'
settingState: '%setting%: %state%'
player:
noMatches: 'No se encontraron jugadores con %target%.'
matches: 'Jugadores con %target%: %detail%'
container:
noMatches: 'No se encontraron contenedores con %target%.'
matches: 'Contenedores con %target%: %detail%'
on: 'activado'
off: 'desactivado'
container:
player: 'Inventario de %player%'
enderchest: 'Cofre de Ender de %player%'
@@ -1,30 +0,0 @@
messages:
error:
consoleUnsupported: 'Voce nao consegue usar esse comando pelo console.'
lootNotGenerated: '&cPilhagem nao gerada! Por favor desabilite &b/silentcontainer&c.'
invalidMaterial: '&cMaterial invalido: "%target%"'
invalidNumber: '&cNumero invalido: "%target%"'
invalidPlayer: '&cJogador nao encontrado!'
permissionOpenSelf: '&cVoce nao pode abrir seu proprio inventario.'
permissionEnderAll: '&cVoce nao tem permissao para abrir baus de ender de outros jogadores.'
permissionExempt: '&cO inventario de %target% e protegido.'
permissionCrossWorld: '&c%target% nao esta no seu mundo.'
permissionPlayerOnline: '&cVoce nao tem permissao para abrir o inventario de jogadores online.'
permissionPlayerOffline: '&cVoce nao tem permissao para abrir o inventario de jogadores offline.'
commandException: '&cUm erro ocorreu. Por favor cheque o console para detalhes.'
info:
containerBlocked: 'Voce esta abrindo um recipiente bloqueado.'
containerBlockedSilent: 'Voce esta abrindo um recipiente bloqueado silenciosamente.'
containerSilent: 'Voce esta abrindo um recipiente silenciosamente.'
settingState: '%setting%: %state%'
player:
noMatches: 'Nenhum jogador encontrado com %target%.'
matches: 'Jogadores segurando %target%: %detail%'
container:
noMatches: 'Nenhum recipiente encontrado com %target%.'
matches: 'Recipientes contendo %target%: %detail%'
on: 'ligado'
off: 'desligado'
container:
player: 'Inventario de %player%'
enderchest: 'Bau de Ender de %player%'
@@ -1,31 +0,0 @@
# Translated into Chinese Simplified by Flandre_tw
messages:
error:
consoleUnsupported: 该命令无法在后台执行。
lootNotGenerated: '&c奖励箱尚未生成 ! 请关闭 &b/silentcontainer&c。'
invalidMaterial: '&c无效的物品 : "%target%"'
invalidNumber: '&c无效的数字 : "%target%"'
invalidPlayer: '&c玩家不存在 !'
permissionOpenSelf: '&c你无法开启自己的物品栏。'
permissionEnderAll: '&c你无法开启其他玩家的末影箱。'
permissionExempt: '&c%target% 的物品栏受到保护。'
permissionCrossWorld: '&c%target% 不在你所在的世界。'
permissionPlayerOnline: '&c你无法开启线上玩家的物品栏。'
permissionPlayerOffline: '&c你无法开启离线玩家的物品栏。'
commandException: '&c发生错误,请查看后台。'
info:
containerBlocked: 你正在开启受阻挡的储物箱。
containerBlockedSilent: 你正在悄悄开启受阻挡的储物箱。
containerSilent: 你正在悄悄开启储物箱。
settingState: '%setting% : %state%'
player:
noMatches: 找不到持有 %target% 的玩家。
matches: '找到持有 %target% 的玩家 : %detail%'
container:
noMatches: 找不到放有 %target% 的储物箱。
matches: '找到放有 %target% 的储物箱 : %detail%'
'true': '开启'
'false': '关闭'
container:
player: '%player% 的物品栏'
enderchest: '%player% 的末影箱'
@@ -1,31 +0,0 @@
# Translated into Chinese Traditional by Flandre_tw
messages:
error:
consoleUnsupported: 該指令無法在控制台執行。
lootNotGenerated: '&c獎勵箱尚未生成 ! 請關閉 &b/silentcontainer&c。'
invalidMaterial: '&c無效的物品 : "%target%"'
invalidNumber: '&c無效的數字 : "%target%"'
invalidPlayer: '&c玩家不存在 !'
permissionOpenSelf: '&c你無法開啟自己的物品欄。'
permissionEnderAll: '&c你無法開啟其他玩家的終界箱。'
permissionExempt: '&c%target% 的物品欄受到保護。'
permissionCrossWorld: '&c%target% 不在你所在的世界。'
permissionPlayerOnline: '&c你無法開啟線上玩家的物品欄。'
permissionPlayerOffline: '&c你無法開啟離線玩家的物品欄。'
commandException: '&c發生錯誤,請查看控制台。'
info:
containerBlocked: 你正在開啟受阻擋的儲物箱。
containerBlockedSilent: 你正在悄悄開啟受阻擋的儲物箱。
containerSilent: 你正在悄悄開啟儲物箱。
settingState: '%setting% : %state%'
player:
noMatches: 找不到持有 %target% 的玩家。
matches: '找到持有 %target% 的玩家 : %detail%'
container:
noMatches: 找不到放有 %target% 的儲物箱。
matches: '找到放有 %target% 的儲物箱 : %detail%'
'true': '開啟'
'false': '關閉'
container:
player: '%player% 的物品欄'
enderchest: '%player% 的終界箱'
-89
View File
@@ -1,89 +0,0 @@
name: OpenInv
main: com.lishid.openinv.OpenInv
version: ${project.version}
author: lishid
authors: [Jikoo, ShadowRanger]
description: >
This plugin allows you to open a player's inventory as a chest and interact with it in real time.
api-version: "1.16"
permissions:
OpenInv.any.default:
description: Permission for AnyContainer to default on prior to toggling.
default: false
OpenInv.silent.default:
description: Permission for SilentContainer to default on prior to toggling.
default: false
OpenInv.*:
description: Permission for all OpenInv features.
default: op
children:
OpenInv.openinv: true
OpenInv.openender: true
OpenInv.search: true
OpenInv.silent: true
OpenInv.anychest: true
OpenInv.searchenchant: true
OpenInv.searchcontainer: true
OpenInv.openonline: true
OpenInv.openoffline: true
OpenInv.spectate: true
OpenInv.openinv:
default: op
children:
OpenInv.openonline: true
OpenInv.openoffline: true
OpenInv.openender:
default: op
children:
OpenInv.openonline: true
OpenInv.openoffline: true
commands:
openinv:
aliases: [oi, inv, open]
description: Open a player's inventory
permission: OpenInv.openinv
usage: |-
/<command> [Player] - Open a player's inventory
openender:
aliases: [oe]
description: Opens the enderchest of a player
permission: OpenInv.openender
usage: |-
/<command> [Player] - Open a player's enderchest
searchinv:
aliases: [si]
description: Search and list players having a specific item
permission: OpenInv.search
usage: |-
/<command> <Material> [MinAmount] - MinAmount is optional, the minimum amount required
searchender:
aliases: [se]
permission: OpenInv.search
description: Searches and lists players having a specific item in their ender chest
usage: |-
/<command> <Material> [MinAmount] - MinAmount is optional, the minimum amount required
silentcontainer:
aliases: [sc, silent, silentchest]
description: SilentContainer stops sounds and animations when using containers.
permission: OpenInv.silent
usage: |-
/<command> [check|on|off] - Check, toggle, or set SilentContainer
anycontainer:
aliases: [ac, anychest]
description: AnyContainer allows using blocked containers.
permission: OpenInv.anychest
usage: |-
/<command> [check|on|off] - Check, toggle, or set AnyContainer
searchenchant:
aliases: [searchenchants]
description: Search and list players with a specific enchantment.
permission: OpenInv.searchenchant
usage: |-
/<command> <[Enchantment] [MinLevel]> - Enchantment is the enchantment type, MinLevel is the minimum level. One is optional
searchcontainer:
aliases: [searchchest]
description: Search and list containers with a specific material.
permission: OpenInv.searchcontainer
usage: /<command> <Material> [ChunkRadius] - ChunkRadius is optional, the length that will be searched for matching items. Default 5
-200
View File
@@ -1,200 +0,0 @@
<!--
~ Copyright (C) 2011-2023 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<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>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<name>OpenInv</name>
<url>http://dev.bukkit.org/bukkit-plugins/openinv/</url>
<version>4.4.2</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.release>17</maven.compiler.release>
<!-- Silence IDE warning - property is declared in individual internal modules. -->
<spigot.version>unknown</spigot.version>
</properties>
<profiles>
<profile>
<id>all</id>
<modules>
<module>api</module>
<module>plugin</module>
<module>internal/v1_19_R3</module>
<module>internal/v1_20_R2</module>
<module>internal/v1_20_R3</module>
<module>assembly</module>
</modules>
</profile>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>api</module>
<module>plugin</module>
<module>assembly</module>
</modules>
</profile>
</profiles>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<artifactId>annotations</artifactId>
<groupId>org.jetbrains</groupId>
<scope>provided</scope>
<version>24.1.0</version>
</dependency>
<dependency>
<artifactId>spigot-api</artifactId>
<groupId>org.spigotmc</groupId>
<scope>provided</scope>
<version>1.18.2-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<artifactId>openinvapi</artifactId>
<groupId>com.lishid</groupId>
<scope>compile</scope>
<version>4.4.2</version>
</dependency>
<dependency>
<artifactId>openinvplugincore</artifactId>
<groupId>com.lishid</groupId>
<scope>compile</scope>
<version>4.4.2</version>
<exclusions>
<exclusion>
<groupId>com.lishid</groupId>
<artifactId>openinvapi</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<!--
~ This plugin is used in scripts to determine if NMS dependencies need to be installed. This must be
~ declared so that we don't run into MDEP-204; the default SuperPOM declares version 2.8.0.
-->
<artifactId>maven-dependency-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.6.1</version>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<filters>
<filter>
<!-- Always shade entirety of required modules. -->
<artifact>com.lishid:openinv*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>*:*</artifact>
<!-- Don't warn about file conflicts that'll be clobbered anyway. -->
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
</excludes>
</filter>
</filters>
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<groupId>org.apache.maven.plugins</groupId>
<version>3.5.1</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.11.0</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.6.0</version>
</plugin>
<plugin>
<groupId>net.md-5</groupId>
<artifactId>specialsource-maven-plugin</artifactId>
<version>1.2.4</version>
<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>
</pluginManagement>
</build>
</project>
-82
View File
@@ -1,82 +0,0 @@
#!/bin/bash
#
# Copyright (C) 2011-2023 lishid. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Note that this script is designed for use in GitHub Actions, and is not
# particularly robust nor configurable. Run from project parent directory.
# Query GitHub for the username of the given email address.
# Falls through to the given author name.
function lookup_email_username() {
lookup=$(curl -G --data-urlencode "q=$1 in:email" https://api.github.com/search/users -H 'Accept: application/vnd.github.v3+json' | grep '"login":' | sed -e 's/^.*": "//g' -e 's/",.*$//g')
if [[ $lookup ]]; then
echo -n "@$lookup"
else
echo "$2"
fi
}
# Get a pretty list of supported Minecraft versions
function get_minecraft_versions() {
readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)"
for version in "${versions[@]}"; do
# Append comma if variable is set, then append version
minecraft_versions="${minecraft_versions:+${minecraft_versions}, }${version%%-R*}"
done
echo "${minecraft_versions}"
}
previous_tag=$(git describe --tags --abbrev=0 @^)
# Use formatted log to pull authors list
authors_raw=$(git log --pretty=format:"%ae|%an" "$previous_tag"..@)
readarray -t authors <<<"$authors_raw"
# Use associative array to map email to author name
declare -A author_data
for author in "${authors[@]}"; do
# Match author email
author_email=${author%|*}
# Convert to lower case
author_email=${author_email,,}
# Match author name
author_name=${author##*|}
if [[ -n ${author_data[$author_email]} ]]; then
# Skip emails we already have data for
continue
fi
# Fetch and store author GitHub username by email
author_data[$author_email]=$(lookup_email_username "$author_email" "$author_name")
done
# Fetch actual formatted changelog
changelog=$(git log --pretty=format:"* %s (%h) - %ae" "$previous_tag"..@)
for author_email in "${!author_data[@]}"; do
# Ignore case when matching
shopt -s nocasematch
# Match and replace email
changelog=${changelog//$author_email/${author_data[$author_email]}}
done
minecraft_versions=$(get_minecraft_versions)
printf "## Supported Minecraft versions\n%s\n\n## Changelog\n%s" "${minecraft_versions}" "${changelog}"
-52
View File
@@ -1,52 +0,0 @@
#!/bin/bash
#
# Copyright (C) 2011-2022 lishid. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Note that this script is designed for use in GitHub Actions, and is not
# particularly robust nor configurable. Run from project parent directory.
# Use a nameref as a cache - maven evaluation is pretty slow.
# Re-calling the script and relying on it to handle caching is way easier than passing around info.
declare -a spigot_versions
# We don't care about concatenation - either it's not null and we handle entries or it's null and we instantiate.
# shellcheck disable=SC2199
if [[ ${spigot_versions[@]} ]]; then
for spigot_version in "${spigot_versions[@]}"; do
echo "$spigot_version"
done
return
fi
old_maven_opts=$MAVEN_OPTS
# Add JVM parameters to allow help plugin access to packages it needs.
export MAVEN_OPTS="$old_maven_opts --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.text=ALL-UNNAMED --add-opens java.desktop/java.awt.font=ALL-UNNAMED"
# Pull Spigot dependency information from Maven.
# Since we only care about Spigot versions, only check modules in the folder internal.
readarray -t modules <<< "$(mvn help:evaluate -Dexpression=project.modules -q -DforceStdout -P all | grep -oP '(?<=<string>)(internal/.*)(?=</string>)')"
declare -n versions="spigot_versions"
for module in "${modules[@]}"; do
# Get Spigot version.
spigot_version=$(mvn help:evaluate -Dexpression=spigot.version -q -DforceStdout -P all -pl "$module")
versions+=("$spigot_version")
echo "$spigot_version"
done
# Reset JVM parameters
export MAVEN_OPTS=$old_maven_opts
-51
View File
@@ -1,51 +0,0 @@
#!/bin/bash
#
# Copyright (C) 2011-2022 lishid. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Note that this script is designed for use in GitHub Actions, and is not
# particularly robust nor configurable. Run from project parent directory.
buildtools_dir=~/buildtools
buildtools=$buildtools_dir/BuildTools.jar
get_buildtools () {
if [[ -d $buildtools_dir && -f $buildtools ]]; then
return
fi
mkdir $buildtools_dir
wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar -O $buildtools
}
readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)"
echo Found Spigot dependencies: "${versions[@]}"
# Install dependencies aside from Spigot prior to running in offline mode.
mvn dependency:go-offline -DexcludeArtifactIds=spigot
for version in "${versions[@]}"; do
set -e
exit_code=0
mvn dependency:get -Dartifact=org.spigotmc:spigot:"$version":remapped-mojang -q -o || exit_code=$?
if [ $exit_code -ne 0 ]; then
echo Installing missing Spigot version "$version"
revision=${version%%-R*}
get_buildtools
java -jar $buildtools -rev "$revision" --remapped
else
echo Spigot "$version" is already installed
fi
done
-55
View File
@@ -1,55 +0,0 @@
#!/bin/bash
#
# Copyright (C) 2011-2021 lishid. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Note that this script is designed for use in GitHub Actions, and is not
# particularly robust nor configurable. Run from project parent directory.
# Parse Spigot dependency information into major Minecraft versions
function get_curseforge_minecraft_versions() {
readarray -t versions <<< "$(. ./scripts/get_spigot_versions.sh)"
for version in "${versions[@]}"; do
# Parse Minecraft major version
version="${version%[.-]"${version#*.*[.-]}"}"
# Skip already listed versions
if [[ "$minecraft_versions" =~ "$version"($|,) ]]; then
continue
fi
# Append comma if variable is set, then append version
minecraft_versions="${minecraft_versions:+${minecraft_versions},}Minecraft ${version}"
done
echo "${minecraft_versions}"
}
# Modify provided changelog to not break when inserted into yaml file.
function get_yaml_safe_changelog() {
changelog=$1
# Since we're using a flow scalar, newlines need to be doubled.
echo "${changelog//
/
}"
}
minecraft_versions=$(get_curseforge_minecraft_versions)
echo "CURSEFORGE_MINECRAFT_VERSIONS=$minecraft_versions" >> "$GITHUB_ENV"
changelog=$(get_yaml_safe_changelog "$1")
printf "CURSEFORGE_CHANGELOG<<EOF\n%s\nEOF\n" "$changelog" >> "$GITHUB_ENV"
-32
View File
@@ -1,32 +0,0 @@
#!/bin/bash
#
# Copyright (C) 2011-2021 lishid. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Note that this script is designed for use in GitHub Actions, and is not
# particularly robust nor configurable. Run from project parent directory.
# Get a pretty string of the project's name and version
# Disable SC warning about variable expansion for this function - those are Maven variables.
# shellcheck disable=SC2016
function get_versioned_name() {
mvn -q -Dexec.executable=echo -Dexec.args='${project.name} ${project.version}' --non-recursive exec:exec
}
# Set GitHub environmental variables
echo "VERSIONED_NAME=$(get_versioned_name)" >> "$GITHUB_ENV"
changelog="$(. ./scripts/generate_changelog.sh)"
printf "GENERATED_CHANGELOG<<EOF\n%s\nEOF\n" "$changelog" >> "$GITHUB_ENV"
-37
View File
@@ -1,37 +0,0 @@
#!/bin/bash
#
# Copyright (C) 2011-2021 lishid. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
if [[ ! $1 ]]; then
echo "Please provide a version string."
return
fi
version="$1"
snapshot="${version%.*}.$((${version##*.} + 1))-SNAPSHOT"
mvn versions:set -DnewVersion="$version"
git add .
git commit -S -m "Bump version to $version for release"
git tag -s "$version" -m "Release $version"
mvn clean package -am -P all
mvn versions:set -DnewVersion="$snapshot"
git add .
git commit -S -m "Bump version to $snapshot for development"
+206
View File
@@ -0,0 +1,206 @@
/*
* 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 java.util.HashMap;
import java.util.logging.Logger;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import com.lishid.openinv.commands.*;
import com.lishid.openinv.internal.IAnySilentChest;
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.utils.UpdateManager;
/**
* Open other player's inventory
*
* @author lishid
*/
public class OpenInv extends JavaPlugin {
public static final Logger logger = Logger.getLogger("Minecraft.OpenInv");
public static HashMap<String, ISpecialPlayerInventory> inventories = new HashMap<String, ISpecialPlayerInventory>();
public static HashMap<String, ISpecialEnderChest> enderChests = new HashMap<String, ISpecialEnderChest>();
private UpdateManager updater = new UpdateManager();
public static OpenInv mainPlugin;
public static IPlayerDataManager playerLoader;
public static IInventoryAccess inventoryAccess;
public static IAnySilentChest anySilentChest;
public void onEnable() {
// Get plugin manager
PluginManager pm = getServer().getPluginManager();
// Version check
boolean success = InternalAccessor.Initialize(this.getServer());
if (!success) {
OpenInv.log("Your version of CraftBukkit is not supported.");
OpenInv.log("Please look for an updated version of OpenInv.");
pm.disablePlugin(this);
return;
}
playerLoader = InternalAccessor.Instance.newPlayerDataManager();
inventoryAccess = InternalAccessor.Instance.newInventoryAccess();
anySilentChest = InternalAccessor.Instance.newAnySilentChest();
mainPlugin = this;
FileConfiguration config = getConfig();
config.set("CheckForUpdates", config.getBoolean("CheckForUpdates", true));
config.set("NotifySilentChest", config.getBoolean("NotifySilentChest", true));
config.set("NotifyAnyChest", config.getBoolean("NotifyAnyChest", true));
config.set("ItemOpenInvItemID", config.getInt("ItemOpenInvItemID", 280));
config.addDefault("ItemOpenInvItemID", 280);
config.addDefault("CheckForUpdates", true);
config.addDefault("NotifySilentChest", true);
config.addDefault("NotifyAnyChest", true);
config.options().copyDefaults(true);
saveConfig();
pm.registerEvents(new OpenInvPlayerListener(), this);
pm.registerEvents(new OpenInvEntityListener(), this);
pm.registerEvents(new OpenInvInventoryListener(), this);
getCommand("openinv").setExecutor(new OpenInvPluginCommand(this));
getCommand("searchinv").setExecutor(new SearchInvPluginCommand());
getCommand("toggleopeninv").setExecutor(new ToggleOpenInvPluginCommand());
getCommand("silentchest").setExecutor(new SilentChestPluginCommand(this));
getCommand("anychest").setExecutor(new AnyChestPluginCommand(this));
getCommand("openender").setExecutor(new OpenEnderPluginCommand(this));
updater.Initialize(this, getFile());
}
public static boolean NotifySilentChest() {
return mainPlugin.getConfig().getBoolean("NotifySilentChest", true);
}
public static boolean NotifyAnyChest() {
return mainPlugin.getConfig().getBoolean("NotifyAnyChest", true);
}
public static boolean GetCheckForUpdates() {
return mainPlugin.getConfig().getBoolean("CheckForUpdates", true);
}
public static boolean GetPlayerItemOpenInvStatus(String name) {
return mainPlugin.getConfig().getBoolean("ItemOpenInv." + name.toLowerCase() + ".toggle", false);
}
public static void SetPlayerItemOpenInvStatus(String name, boolean status) {
mainPlugin.getConfig().set("ItemOpenInv." + name.toLowerCase() + ".toggle", status);
mainPlugin.saveConfig();
}
public static boolean GetPlayerSilentChestStatus(String name) {
return mainPlugin.getConfig().getBoolean("SilentChest." + name.toLowerCase() + ".toggle", false);
}
public static void SetPlayerSilentChestStatus(String name, boolean status) {
mainPlugin.getConfig().set("SilentChest." + name.toLowerCase() + ".toggle", status);
mainPlugin.saveConfig();
}
public static boolean GetPlayerAnyChestStatus(String name) {
return mainPlugin.getConfig().getBoolean("AnyChest." + name.toLowerCase() + ".toggle", true);
}
public static void SetPlayerAnyChestStatus(String name, boolean status) {
mainPlugin.getConfig().set("AnyChest." + name.toLowerCase() + ".toggle", status);
mainPlugin.saveConfig();
}
public static int GetItemOpenInvItem() {
if (mainPlugin.getConfig().get("ItemOpenInvItemID") == null) {
SaveToConfig("ItemOpenInvItemID", 280);
}
return mainPlugin.getConfig().getInt("ItemOpenInvItemID", 280);
}
public static Object GetFromConfig(String data, Object defaultValue) {
Object val = mainPlugin.getConfig().get(data);
if (val == null) {
mainPlugin.getConfig().set(data, defaultValue);
return defaultValue;
}
else {
return val;
}
}
public static void SaveToConfig(String data, Object value) {
mainPlugin.getConfig().set(data, value);
mainPlugin.saveConfig();
}
/**
* Log an information
*/
public static void log(String text) {
logger.info("[OpenInv] " + text);
}
/**
* Log an error
*/
public static void log(Throwable e) {
logger.severe("[OpenInv] " + e.toString());
e.printStackTrace();
}
public static void ShowHelp(Player player) {
player.sendMessage(ChatColor.GREEN + "/openinv <Player> - Open a player's inventory");
player.sendMessage(ChatColor.GREEN + " (aliases: oi, inv, open)");
player.sendMessage(ChatColor.GREEN + "/openender <Player> - Open a player's enderchest");
player.sendMessage(ChatColor.GREEN + " (aliases: oe, enderchest)");
player.sendMessage(ChatColor.GREEN + "/toggleopeninv - Toggle item openinv function");
player.sendMessage(ChatColor.GREEN + " (aliases: toi, toggleoi, toggleinv)");
player.sendMessage(ChatColor.GREEN + "/searchinv <Item> [MinAmount] - ");
player.sendMessage(ChatColor.GREEN + " Search and list players having a specific item.");
player.sendMessage(ChatColor.GREEN + " (aliases: si, search)");
player.sendMessage(ChatColor.GREEN + "/anychest - Toggle anychest function");
player.sendMessage(ChatColor.GREEN + " (aliases: ac)");
player.sendMessage(ChatColor.GREEN + "/silentchest - Toggle silent chest function");
player.sendMessage(ChatColor.GREEN + " (aliases: sc, silent)");
}
public static boolean hasPermission(Permissible player, String permission) {
String[] parts = permission.split("\\.");
String perm = "";
for (int i = 0; i < parts.length; i++) {
if (player.hasPermission(perm + "*")) {
return true;
}
perm += parts[i] + ".";
}
return player.hasPermission(permission);
}
}
@@ -0,0 +1,52 @@
/*
* 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.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
public class OpenInvEntityListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
public void onEntityDamage(EntityDamageEvent event) {
if (event instanceof EntityDamageByEntityEvent) {
EntityDamageByEntityEvent evt = (EntityDamageByEntityEvent) event;
Entity attacker = evt.getDamager();
Entity defender = evt.getEntity();
if (!(attacker instanceof Player) || !(defender instanceof Player)) {
return;
}
Player player = (Player) attacker;
if (!(player.getItemInHand().getType().getId() == OpenInv.GetItemOpenInvItem()) || (!OpenInv.GetPlayerItemOpenInvStatus(player.getName())) || !OpenInv.hasPermission(player, "OpenInv.openinv")) {
return;
}
Player target = (Player) defender;
player.performCommand("openinv " + target.getName());
evt.setDamage(0);
evt.setCancelled(true);
}
}
}
@@ -0,0 +1,35 @@
/*
* 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.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
public class OpenInvInventoryListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL)
public void onInventoryClick(InventoryClickEvent event) {
// If this is the top inventory
// if (event.getView().convertSlot(event.getRawSlot()) == event.getRawSlot())
// {
if (!OpenInv.inventoryAccess.check(event.getInventory(), event.getWhoClicked())) {
event.setCancelled(true);
}
// }
}
}
@@ -0,0 +1,133 @@
/*
* 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.ChatColor;
import org.bukkit.block.Chest;
import org.bukkit.block.Sign;
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;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
public class OpenInvPlayerListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent event) {
ISpecialPlayerInventory inventory = OpenInv.inventories.get(event.getPlayer().getName().toLowerCase());
if (inventory != null) {
inventory.PlayerGoOnline(event.getPlayer());
}
ISpecialEnderChest chest = OpenInv.enderChests.get(event.getPlayer().getName().toLowerCase());
if (chest != null) {
chest.PlayerGoOnline(event.getPlayer());
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
ISpecialPlayerInventory inventory = OpenInv.inventories.get(event.getPlayer().getName().toLowerCase());
if (inventory != null) {
inventory.PlayerGoOffline();
}
ISpecialEnderChest chest = OpenInv.enderChests.get(event.getPlayer().getName().toLowerCase());
if (chest != null) {
chest.PlayerGoOffline();
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
if (event.getPlayer().isSneaking()) {
return;
}
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.useInteractedBlock() == Result.DENY) {
return;
}
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getType() == org.bukkit.Material.ENDER_CHEST) {
if (OpenInv.hasPermission(player, Permissions.PERM_SILENT) && OpenInv.GetPlayerSilentChestStatus(player.getName())) {
event.setCancelled(true);
player.openInventory(player.getEnderChest());
}
}
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getState() instanceof Chest) {
boolean silentchest = false;
boolean anychest = false;
int x = event.getClickedBlock().getX();
int y = event.getClickedBlock().getY();
int z = event.getClickedBlock().getZ();
if (OpenInv.hasPermission(player, Permissions.PERM_SILENT) && OpenInv.GetPlayerSilentChestStatus(player.getName())) {
silentchest = true;
}
if (OpenInv.hasPermission(player, Permissions.PERM_ANYCHEST) && OpenInv.GetPlayerAnyChestStatus(player.getName())) {
try {
anychest = OpenInv.anySilentChest.IsAnyChestNeeded(player, x, y, z);
}
catch (Exception e) {
player.sendMessage(ChatColor.RED + "Error while executing openinv. Unsupported CraftBukkit.");
e.printStackTrace();
}
}
// If the anychest or silentchest is active
if (anychest || silentchest) {
if (!OpenInv.anySilentChest.ActivateChest(player, anychest, silentchest, x, y, z)) {
event.setCancelled(true);
}
}
}
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getState() instanceof Sign) {
try {
Sign sign = ((Sign) event.getClickedBlock().getState());
if (OpenInv.hasPermission(player, Permissions.PERM_OPENINV) && sign.getLine(0).equalsIgnoreCase("[openinv]")) {
String text = sign.getLine(1).trim() + sign.getLine(2).trim() + sign.getLine(3).trim();
player.performCommand("openinv " + text);
}
}
catch (Exception ex) {
player.sendMessage("Internal Error.");
ex.printStackTrace();
}
}
if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
if (!(player.getItemInHand().getType().getId() == OpenInv.GetItemOpenInvItem()) || (!OpenInv.GetPlayerItemOpenInvStatus(player.getName())) || !OpenInv.hasPermission(player, Permissions.PERM_OPENINV)) {
return;
}
player.performCommand("openinv");
}
}
}
+16
View File
@@ -0,0 +1,16 @@
package com.lishid.openinv;
public class Permissions {
public static final String PERM_OPENINV = "OpenInv.openinv";
public static final String PERM_OVERRIDE = "OpenInv.override";
public static final String PERM_EXEMPT = "OpenInv.exempt";
public static final String PERM_CROSSWORLD = "OpenInv.crossworld";
public static final String PERM_SILENT = "OpenInv.silent";
public static final String PERM_ANYCHEST = "OpenInv.anychest";
public static final String PERM_ENDERCHEST = "OpenInv.openender";
public static final String PERM_ENDERCHEST_ALL = "OpenInv.openenderall";
public static final String PERM_SEARCH = "OpenInv.search";
public static final String PERM_EDITINV = "OpenInv.editinv";
public static final String PERM_EDITENDER = "OpenInv.editender";
public static final String PERM_OPENSELF = "OpenInv.openself";
}
@@ -0,0 +1,57 @@
/*
* 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;
import com.lishid.openinv.Permissions;
public class AnyChestPluginCommand implements CommandExecutor {
public AnyChestPluginCommand(OpenInv 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;
}
if (!OpenInv.hasPermission(sender, Permissions.PERM_ANYCHEST)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to use anychest.");
return true;
}
if (args.length > 0) {
if (args[0].equalsIgnoreCase("check")) {
if (OpenInv.GetPlayerAnyChestStatus(sender.getName()))
sender.sendMessage("AnyChest is ON.");
else
sender.sendMessage("AnyChest is OFF.");
}
}
OpenInv.SetPlayerAnyChestStatus(sender.getName(), !OpenInv.GetPlayerAnyChestStatus(sender.getName()));
sender.sendMessage("AnyChest is now " + (OpenInv.GetPlayerAnyChestStatus(sender.getName()) ? "On" : "Off") + ".");
return true;
}
}
@@ -0,0 +1,140 @@
/*
* 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 java.util.UUID;
import org.bukkit.Bukkit;
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;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.InternalAccessor;
public class OpenEnderPluginCommand implements CommandExecutor {
private final OpenInv plugin;
public static HashMap<Player, String> openEnderHistory = new HashMap<Player, String>();
public OpenEnderPluginCommand(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;
}
if (!OpenInv.hasPermission(sender, Permissions.PERM_ENDERCHEST)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to access player enderchest");
return true;
}
if (args.length > 0 && args[0].equalsIgnoreCase("?")) {
OpenInv.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];
}
sender.sendMessage(ChatColor.GREEN + "Starting inventory lookup.");
final UUID senderID = player.getUniqueId();
new BukkitRunnable() {
@Override
public void run() {
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(name);
if (Bukkit.getPlayer(senderID) == null) {
return;
}
if (offlinePlayer == null || !offlinePlayer.hasPlayedBefore()) {
player.sendMessage(ChatColor.RED + "Player not found!");
return;
}
new BukkitRunnable() {
@Override
public void run() {
if (Bukkit.getPlayer(senderID) == null) {
return;
}
openInventory(player, offlinePlayer.getUniqueId());
}
}.runTask(plugin);
}
}.runTaskAsynchronously(plugin);
return true;
}
private void openInventory(Player player, UUID uuid) {
Player target = this.plugin.getServer().getPlayer(uuid);
if (target == null) {
// Try loading the player's data
target = OpenInv.playerLoader.loadPlayer(uuid);
if (target == null) {
player.sendMessage(ChatColor.RED + "Player not found!");
return;
}
}
if (target != player && !OpenInv.hasPermission(player, Permissions.PERM_ENDERCHEST_ALL)) {
player.sendMessage(ChatColor.RED + "You do not have permission to access other player's enderchest");
return;
}
// Record the target
openEnderHistory.put(player, target.getName());
// Create the inventory
ISpecialEnderChest chest = OpenInv.enderChests.get(target.getName().toLowerCase());
if (chest == null) {
chest = InternalAccessor.Instance.newSpecialEnderChest(target, !target.isOnline());
}
// Open the inventory
player.openInventory(chest.getBukkitInventory());
}
}
@@ -0,0 +1,151 @@
/*
* 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 java.util.UUID;
import org.bukkit.Bukkit;
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;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.internal.InternalAccessor;
public class OpenInvPluginCommand implements CommandExecutor {
private final OpenInv plugin;
public static HashMap<Player, String> openInvHistory = new HashMap<Player, String>();
public OpenInvPluginCommand(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;
}
if (!OpenInv.hasPermission(sender, Permissions.PERM_OPENINV)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to access player inventories");
return true;
}
if (args.length > 0 && args[0].equalsIgnoreCase("?")) {
OpenInv.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];
}
sender.sendMessage(ChatColor.GREEN + "Starting inventory lookup.");
final UUID senderID = player.getUniqueId();
new BukkitRunnable() {
@Override
public void run() {
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(name);
if (Bukkit.getPlayer(senderID) == null) {
return;
}
if (offlinePlayer == null || !offlinePlayer.hasPlayedBefore()) {
player.sendMessage(ChatColor.RED + "Player not found!");
return;
}
new BukkitRunnable() {
@Override
public void run() {
if (Bukkit.getPlayer(senderID) == null) {
return;
}
openInventory(player, offlinePlayer.getUniqueId());
}
}.runTask(plugin);
}
}.runTaskAsynchronously(plugin);
return true;
}
private void openInventory(Player player, UUID uuid) {
Player target = this.plugin.getServer().getPlayer(uuid);
if (target == null) {
// Try loading the player's data
target = OpenInv.playerLoader.loadPlayer(uuid);
if (target == null) {
player.sendMessage(ChatColor.RED + "Player not found!");
return;
}
}
// Permissions checks
if (!OpenInv.hasPermission(player, Permissions.PERM_OVERRIDE) && OpenInv.hasPermission(target, Permissions.PERM_EXEMPT)) {
player.sendMessage(ChatColor.RED + target.getDisplayName() + "'s inventory is protected!");
return;
}
// Crosswork check
if ((!OpenInv.hasPermission(player, Permissions.PERM_CROSSWORLD) && !OpenInv.hasPermission(player, Permissions.PERM_OVERRIDE)) && target.getWorld() != player.getWorld()) {
player.sendMessage(ChatColor.RED + target.getDisplayName() + " is not in your world!");
return;
}
// Self-open check
if (!OpenInv.hasPermission(player, Permissions.PERM_OPENSELF) && target.equals(player)) {
player.sendMessage(ChatColor.RED + "You're not allowed to openinv yourself.");
return;
}
// Record the target
openInvHistory.put(player, target.getName());
// Create the inventory
ISpecialPlayerInventory inv = OpenInv.inventories.get(target.getName().toLowerCase());
if (inv == null) {
inv = InternalAccessor.Instance.newSpecialPlayerInventory(target, !target.isOnline());
}
// Open the inventory
player.openInventory(inv.getBukkitInventory());
}
}
@@ -0,0 +1,77 @@
/*
* 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 com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
public class SearchInvPluginCommand implements CommandExecutor {
public SearchInvPluginCommand() {
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender instanceof Player) {
if (!OpenInv.hasPermission(sender, Permissions.PERM_SEARCH)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to access player inventories");
return true;
}
}
String PlayerList = "";
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;
}
for (Player templayer : Bukkit.getServer().getOnlinePlayers()) {
if (templayer.getInventory().contains(material, count)) {
PlayerList += templayer.getName() + " ";
}
}
sender.sendMessage("Players with the item " + material.toString() + ": " + PlayerList);
return true;
}
}
@@ -0,0 +1,57 @@
/*
* 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;
import com.lishid.openinv.Permissions;
public class SilentChestPluginCommand implements CommandExecutor {
public SilentChestPluginCommand(OpenInv 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;
}
if (!OpenInv.hasPermission(sender, Permissions.PERM_SILENT)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to use silent chest.");
return true;
}
if (args.length > 0) {
if (args[0].equalsIgnoreCase("check")) {
if (OpenInv.GetPlayerSilentChestStatus(sender.getName()))
sender.sendMessage("SilentChest is ON.");
else
sender.sendMessage("SilentChest is OFF.");
}
}
OpenInv.SetPlayerSilentChestStatus(sender.getName(), !OpenInv.GetPlayerSilentChestStatus(sender.getName()));
sender.sendMessage("SilentChest is now " + (OpenInv.GetPlayerSilentChestStatus(sender.getName()) ? "On" : "Off") + ".");
return true;
}
}
@@ -0,0 +1,60 @@
/*
* 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.Material;
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;
import com.lishid.openinv.Permissions;
public class ToggleOpenInvPluginCommand implements CommandExecutor {
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 (!OpenInv.hasPermission(sender, Permissions.PERM_OPENINV)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to access player inventories");
return true;
}
Player player = (Player) sender;
if (args.length > 0) {
if (args[0].equalsIgnoreCase("check")) {
if (OpenInv.GetPlayerItemOpenInvStatus(player.getName()))
player.sendMessage("OpenInv with " + Material.getMaterial(OpenInv.GetItemOpenInvItem()).toString() + " is ON.");
else
player.sendMessage("OpenInv with " + Material.getMaterial(OpenInv.GetItemOpenInvItem()).toString() + " is OFF.");
}
}
if (OpenInv.GetPlayerItemOpenInvStatus(player.getName())) {
OpenInv.SetPlayerItemOpenInvStatus(player.getName(), false);
player.sendMessage("OpenInv with " + Material.getMaterial(OpenInv.GetItemOpenInvItem()).toString() + " is OFF.");
}
else {
OpenInv.SetPlayerItemOpenInvStatus(player.getName(), true);
player.sendMessage("OpenInv with " + Material.getMaterial(OpenInv.GetItemOpenInvItem()).toString() + " is ON.");
}
return true;
}
}
@@ -0,0 +1,25 @@
/*
* 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;
public interface IAnySilentChest {
public boolean IsAnyChestNeeded(Player p, int x, int y, int z);
public boolean ActivateChest(Player p, boolean anychest, boolean silentchest, int x, int y, int z);
}
@@ -1,24 +1,24 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
/**
* An {@link ISpecialInventory} representing an ender chest.
*/
public interface ISpecialEnderChest extends ISpecialInventory {
}
/*
* 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.HumanEntity;
import org.bukkit.inventory.Inventory;
public interface IInventoryAccess {
public boolean check(Inventory inventory, HumanEntity player);
}
@@ -0,0 +1,26 @@
/*
* 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.util.UUID;
import org.bukkit.entity.Player;
public interface IPlayerDataManager {
public Player loadPlayer(String name);
public Player loadPlayer(UUID uuid);
}
@@ -0,0 +1,31 @@
/*
* 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 {
public Inventory getBukkitInventory();
public void InventoryRemovalCheck();
public void PlayerGoOnline(Player p);
public void PlayerGoOffline();
}
@@ -0,0 +1,30 @@
/*
* 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 {
public Inventory getBukkitInventory();
public void InventoryRemovalCheck();
public void PlayerGoOnline(Player p);
public void PlayerGoOffline();
}
@@ -0,0 +1,105 @@
/*
* 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.Server;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
public class InternalAccessor {
public static InternalAccessor Instance;
private String version;
/*
* Returns false if version not supported
*/
public static boolean Initialize(Server server) {
Instance = new InternalAccessor();
String packageName = server.getClass().getPackage().getName();
Instance.version = packageName.substring(packageName.lastIndexOf('.') + 1);
try {
Class.forName("com.lishid.openinv.internal." + Instance.version + ".AnySilentChest");
return true;
}
catch (Exception e) {
return false;
}
}
public void PrintError() {
OpenInv.log("OpenInv encountered an error with the CraftBukkit version \"" + Instance.version + "\". Please look for an updated version of OpenInv.");
}
public IPlayerDataManager newPlayerDataManager() {
return (IPlayerDataManager) createObject(IPlayerDataManager.class, "PlayerDataManager");
}
public IInventoryAccess newInventoryAccess() {
return (IInventoryAccess) createObject(IInventoryAccess.class, "InventoryAccess");
}
public IAnySilentChest newAnySilentChest() {
return (IAnySilentChest) createObject(IAnySilentChest.class, "AnySilentChest");
}
public ISpecialPlayerInventory newSpecialPlayerInventory(Player player, boolean offline) {
try {
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + version + ".SpecialPlayerInventory");
if (ISpecialPlayerInventory.class.isAssignableFrom(internalClass)) {
return (ISpecialPlayerInventory) internalClass.getConstructor(Player.class, Boolean.class).newInstance(player, offline);
}
}
catch (Exception e) {
PrintError();
OpenInv.log(e);
}
return null;
}
public ISpecialEnderChest newSpecialEnderChest(Player player, boolean offline) {
try {
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + version + ".SpecialEnderChest");
if (ISpecialEnderChest.class.isAssignableFrom(internalClass)) {
return (ISpecialEnderChest) internalClass.getConstructor(Player.class, Boolean.class).newInstance(player, offline);
}
}
catch (Exception e) {
PrintError();
OpenInv.log(e);
}
return null;
}
private Object createObject(Class<? extends Object> assignableClass, String className) {
try {
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + version + "." + className);
if (assignableClass.isAssignableFrom(internalClass)) {
return internalClass.getConstructor().newInstance();
}
}
catch (Exception e) {
PrintError();
OpenInv.log(e);
}
return null;
}
}
@@ -0,0 +1,119 @@
/*
* 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_8_R1;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentChest;
//Volatile
import net.minecraft.server.v1_8_R1.*;
import org.bukkit.craftbukkit.v1_8_R1.entity.*;
public class AnySilentChest implements IAnySilentChest {
public boolean IsAnyChestNeeded(Player p, int x, int y, int z) {
// FOR REFERENCE, LOOK AT net.minecraft.server.BlockChest
EntityPlayer player = ((CraftPlayer) p).getHandle();
World world = player.world;
// If block on top
if (world.getType(new BlockPosition(x, y + 1, z)).getBlock().c())
return true;
int id = Block.getId(world.getType(new BlockPosition(x, y, z)).getBlock());
// If block next to chest is chest and has a block on top
if ((Block.getId(world.getType(new BlockPosition(x - 1, y, z)).getBlock()) == id) && (world.getType(new BlockPosition(x - 1, y + 1, z)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x + 1, y, z)).getBlock()) == id) && (world.getType(new BlockPosition(x + 1, y + 1, z)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x, y, z - 1)).getBlock()) == id) && (world.getType(new BlockPosition(x, y + 1, z - 1)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x, y, z + 1)).getBlock()) == id) && (world.getType(new BlockPosition(x, y + 1, z + 1)).getBlock().c()))
return true;
return false;
}
public boolean ActivateChest(Player p, boolean anychest, boolean silentchest, int x, int y, int z) {
EntityPlayer player = ((CraftPlayer) p).getHandle();
World world = player.world;
Object chest = (TileEntityChest) world.getTileEntity(new BlockPosition(x, y, z));
if (chest == null)
return true;
int id = Block.getId(world.getType(new BlockPosition(x, y, z)).getBlock());
if (!anychest) {
if (world.getType(new BlockPosition(x, y + 1, z)).getBlock().c())
return true;
if ((Block.getId(world.getType(new BlockPosition(x - 1, y, z)).getBlock()) == id) && (world.getType(new BlockPosition(x - 1, y + 1, z)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x + 1, y, z)).getBlock()) == id) && (world.getType(new BlockPosition(x + 1, y + 1, z)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x, y, z - 1)).getBlock()) == id) && (world.getType(new BlockPosition(x, y + 1, z - 1)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x, y, z + 1)).getBlock()) == id) && (world.getType(new BlockPosition(x, y + 1, z + 1)).getBlock().c()))
return true;
}
if (Block.getId(world.getType(new BlockPosition(x - 1, y, z)).getBlock()) == id)
chest = new InventoryLargeChest("Large chest", (TileEntityChest) world.getTileEntity(new BlockPosition(x - 1, y, z)), (ITileInventory) chest);
if (Block.getId(world.getType(new BlockPosition(x + 1, y, z)).getBlock()) == id)
chest = new InventoryLargeChest("Large chest", (ITileInventory) chest, (TileEntityChest) world.getTileEntity(new BlockPosition(x + 1, y, z)));
if (Block.getId(world.getType(new BlockPosition(x, y, z - 1)).getBlock()) == id)
chest = new InventoryLargeChest("Large chest", (TileEntityChest) world.getTileEntity(new BlockPosition(x, y, z - 1)), (ITileInventory) chest);
if (Block.getId(world.getType(new BlockPosition(x, y, z + 1)).getBlock()) == id)
chest = new InventoryLargeChest("Large chest", (ITileInventory) chest, (TileEntityChest) world.getTileEntity(new BlockPosition(x, y, z + 1)));
boolean returnValue = true;
if (!silentchest) {
player.openContainer((IInventory) chest);
}
else {
try {
SilentContainerChest silentContainerChest = new SilentContainerChest(player.inventory, ((IInventory) chest), player);
int windowId = player.nextContainerCounter();
player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(windowId, "minecraft:chest", ((IInventory) chest).getScoreboardDisplayName(), ((IInventory) chest).getSize()));
player.activeContainer = silentContainerChest;
player.activeContainer.windowId = windowId;
player.activeContainer.addSlotListener(player);
if (OpenInv.NotifySilentChest()) {
p.sendMessage("You are opening a chest silently.");
}
returnValue = false;
}
catch (Exception e) {
e.printStackTrace();
p.sendMessage(ChatColor.RED + "Error while sending silent chest.");
}
}
if (anychest && OpenInv.NotifyAnyChest()) {
p.sendMessage("You are opening a blocked chest.");
}
return returnValue;
}
}
@@ -0,0 +1,72 @@
/*
* 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_8_R1;
import java.lang.reflect.Field;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.Inventory;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
import com.lishid.openinv.internal.IInventoryAccess;
//Volatile
import net.minecraft.server.v1_8_R1.*;
import org.bukkit.craftbukkit.v1_8_R1.inventory.*;
public class InventoryAccess implements IInventoryAccess {
public boolean check(Inventory inventory, HumanEntity player) {
IInventory inv = grabInventory(inventory);
if (inv instanceof SpecialPlayerInventory) {
if (!OpenInv.hasPermission(player, Permissions.PERM_EDITINV)) {
return false;
}
}
else if (inv instanceof SpecialEnderChest) {
if (!OpenInv.hasPermission(player, Permissions.PERM_EDITENDER)) {
return false;
}
}
return true;
}
private IInventory grabInventory(Inventory inventory) {
if(inventory instanceof CraftInventory) {
return ((CraftInventory) inventory).getInventory();
}
//Use reflection to find the iiventory
Class<? extends Inventory> clazz = inventory.getClass();
IInventory result = null;
for(Field f : clazz.getDeclaredFields()) {
f.setAccessible(true);
if(IInventory.class.isAssignableFrom(f.getDeclaringClass())) {
try {
result = (IInventory) f.get(inventory);
}
catch (Exception e) {
OpenInv.log(e);
}
}
}
return result;
}
}
@@ -0,0 +1,129 @@
/*
* 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_8_R1;
import java.io.File;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.mojang.authlib.GameProfile;
//Volatile
import net.minecraft.server.v1_8_R1.*;
import org.bukkit.craftbukkit.v1_8_R1.*;
public class PlayerDataManager implements IPlayerDataManager {
public Player loadPlayer(String name) {
try {
UUID uuid = matchUser(name);
if (uuid == null) {
return null;
}
// Default player folder
File playerfolder = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "playerdata");
if (!playerfolder.exists()) {
return null;
}
OfflinePlayer player = Bukkit.getOfflinePlayer(uuid);
if (player == null) {
return null;
}
GameProfile profile = new GameProfile(uuid, player.getName());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
// Create an entity to load the player data
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), profile, 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;
}
}
catch (Exception e) {
OpenInv.log(e);
}
return null;
}
private static UUID matchUser(String search) {
UUID found = null;
String lowerSearch = search.toLowerCase();
int delta = 2147483647;
OfflinePlayer[] offlinePlayers = Bukkit.getOfflinePlayers();
for (OfflinePlayer player : offlinePlayers) {
String name = player.getName();
if (name == null){
continue;
}
if (name.equalsIgnoreCase(search)){
return player.getUniqueId();
}
if (name.toLowerCase().startsWith(lowerSearch)) {
int curDelta = name.length() - lowerSearch.length();
if (curDelta < delta) {
found = player.getUniqueId();
delta = curDelta;
}
if (curDelta == 0) {
break;
}
}
}
return found;
}
/* (non-Javadoc)
* @see com.lishid.openinv.internal.IPlayerDataManager#loadPlayer(java.util.UUID)
*/
@Override
public Player loadPlayer(UUID uuid) {
OfflinePlayer player = Bukkit.getOfflinePlayer(uuid);
if (player == null || !player.hasPlayedBefore()) {
return null;
}
GameProfile profile = new GameProfile(uuid, player.getName());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
// Create an entity to load the player data
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), profile, 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;
}
return null;
}
}
@@ -0,0 +1,36 @@
/*
* 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_8_R1;
//Volatile
import net.minecraft.server.v1_8_R1.*;
public class SilentContainerChest extends ContainerChest {
public IInventory inv;
public SilentContainerChest(IInventory i1, IInventory i2, EntityHuman e1) {
super(i1, i2, e1);
inv = i2;
// close signal
inv.closeContainer(e1);
}
@Override
public void b(EntityHuman paramEntityHuman) {
// Don't send close signal twice, might screw up
}
}
@@ -0,0 +1,126 @@
/*
* 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_8_R1;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import com.lishid.openinv.OpenInv;
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_8_R1.*;
import org.bukkit.craftbukkit.v1_8_R1.entity.*;
import org.bukkit.craftbukkit.v1_8_R1.inventory.*;
public class SpecialEnderChest extends InventorySubcontainer implements IInventory, ISpecialEnderChest {
public List<HumanEntity> transaction = new ArrayList<HumanEntity>();
public boolean playerOnline = false;
private CraftPlayer owner;
private InventoryEnderChest enderChest;
private int maxStack = MAX_STACK;
private CraftInventory inventory = new CraftInventory(this);
public SpecialEnderChest(Player p, Boolean online) {
super(((CraftPlayer) p).getHandle().getEnderChest().getName(), ((CraftPlayer) p).getHandle().getEnderChest().hasCustomName(), ((CraftPlayer) p).getHandle().getEnderChest().getSize());
CraftPlayer player = (CraftPlayer) p;
this.enderChest = player.getHandle().getEnderChest();
this.owner = player;
this.items = enderChest.getContents();
OpenInv.enderChests.put(owner.getName().toLowerCase(), this);
}
public Inventory getBukkitInventory() {
return inventory;
}
public void InventoryRemovalCheck() {
owner.saveData();
if (transaction.isEmpty() && !playerOnline) {
OpenInv.enderChests.remove(owner.getName().toLowerCase());
}
}
public void PlayerGoOnline(Player p) {
if (!playerOnline) {
try {
InventoryEnderChest playerEnderChest = ((CraftPlayer) p).getHandle().getEnderChest();
Field field = playerEnderChest.getClass().getField("items");
field.setAccessible(true);
field.set(playerEnderChest, this.items);
}
catch (Exception e) {}
p.saveData();
playerOnline = true;
}
}
public void PlayerGoOffline() {
playerOnline = false;
}
public ItemStack[] getContents() {
return this.items;
}
public void onOpen(CraftHumanEntity who) {
transaction.add(who);
}
public void onClose(CraftHumanEntity who) {
transaction.remove(who);
this.InventoryRemovalCheck();
}
public List<HumanEntity> getViewers() {
return transaction;
}
public InventoryHolder getOwner() {
return this.owner;
}
public void setMaxStackSize(int size) {
maxStack = size;
}
public int getMaxStackSize() {
return maxStack;
}
public boolean a(EntityHuman entityhuman) {
return true;
}
public void startOpen() {
}
public void f() {
}
public void update() {
enderChest.update();
}
}
@@ -0,0 +1,253 @@
/*
* 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_8_R1;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
//Volatile
import net.minecraft.server.v1_8_R1.*;
import org.bukkit.craftbukkit.v1_8_R1.entity.*;
import org.bukkit.craftbukkit.v1_8_R1.inventory.*;
public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory {
CraftPlayer owner;
public boolean playerOnline = false;
private ItemStack[] extra = new ItemStack[5];
private CraftInventory inventory = new CraftInventory(this);
public SpecialPlayerInventory(Player p, Boolean online) {
super(((CraftPlayer) p).getHandle());
this.owner = ((CraftPlayer) p);
this.playerOnline = online;
this.items = player.inventory.items;
this.armor = player.inventory.armor;
OpenInv.inventories.put(owner.getName().toLowerCase(), this);
}
@Override
public Inventory getBukkitInventory() {
return inventory;
}
@Override
public void InventoryRemovalCheck() {
owner.saveData();
if (transaction.isEmpty() && !playerOnline) {
OpenInv.inventories.remove(owner.getName().toLowerCase());
}
}
@Override
public void PlayerGoOnline(Player player) {
if (!playerOnline) {
CraftPlayer p = (CraftPlayer) player;
p.getHandle().inventory.items = this.items;
p.getHandle().inventory.armor = this.armor;
p.saveData();
playerOnline = true;
}
}
@Override
public void PlayerGoOffline() {
playerOnline = false;
this.InventoryRemovalCheck();
}
@Override
public void onClose(CraftHumanEntity who) {
super.onClose(who);
this.InventoryRemovalCheck();
}
@Override
public ItemStack[] getContents() {
ItemStack[] C = new ItemStack[getSize()];
System.arraycopy(items, 0, C, 0, items.length);
System.arraycopy(armor, 0, C, items.length, armor.length);
return C;
}
@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) {
owner.getHandle().drop(itemstack, true);
itemstack = null;
}
is[i] = itemstack;
owner.getHandle().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();
}
@Override
public boolean a(EntityHuman entityhuman) {
return true;
}
}
@@ -0,0 +1,510 @@
package com.lishid.openinv.utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Bukkit;
public final class ReflectionUtil {
private ReflectionUtil() {}
public static Class<?> getClass(String name, PackageType type) throws Exception {
return Class.forName(type + "." + name);
}
public static Class<?> getClass(String name, SubPackageType type) throws Exception {
return Class.forName(type + "." + name);
}
public static Constructor<?> getConstructor(Class<?> clazz, Class<?>... parameterTypes) {
Class<?>[] p = DataType.convertToPrimitive(parameterTypes);
for (Constructor<?> c : clazz.getConstructors())
if (DataType.equalsArray(DataType.convertToPrimitive(c.getParameterTypes()), p))
return c;
return null;
}
public static Constructor<?> getConstructor(String className, PackageType type, Class<?>... parameterTypes) throws Exception {
return getConstructor(getClass(className, type), parameterTypes);
}
public static Constructor<?> getConstructor(String className, SubPackageType type, Class<?>... parameterTypes) throws Exception {
return getConstructor(getClass(className, type), parameterTypes);
}
public static Object newInstance(Class<?> clazz, Object... args) throws Exception {
return getConstructor(clazz, DataType.convertToPrimitive(args)).newInstance(args);
}
public static Object newInstance(String className, PackageType type, Object... args) throws Exception {
return newInstance(getClass(className, type), args);
}
public static Object newInstance(String className, SubPackageType type, Object... args) throws Exception {
return newInstance(getClass(className, type), args);
}
public static Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
Class<?>[] p = DataType.convertToPrimitive(parameterTypes);
for (Method m : clazz.getMethods())
if (m.getName().equals(name) && DataType.equalsArray(DataType.convertToPrimitive(m.getParameterTypes()), p))
return m;
return null;
}
public static Method getMethod(String className, PackageType type, String name, Class<?>... parameterTypes) throws Exception {
return getMethod(getClass(className, type), name, parameterTypes);
}
public static Method getMethod(String className, SubPackageType type, String name, Class<?>... parameterTypes) throws Exception {
return getMethod(getClass(className, type), name, parameterTypes);
}
public static Object invokeMethod(String name, Object instance, Object... args) throws Exception {
return getMethod(instance.getClass(), name, DataType.convertToPrimitive(args)).invoke(instance, args);
}
public static Object invokeMethod(Class<?> clazz, String name, Object instance, Object... args) throws Exception {
return getMethod(clazz, name, DataType.convertToPrimitive(args)).invoke(instance, args);
}
public static Object invokeMethod(String className, PackageType type, String name, Object instance, Object... args) throws Exception {
return invokeMethod(getClass(className, type), name, instance, args);
}
public static Object invokeMethod(String className, SubPackageType type, String name, Object instance, Object... args) throws Exception {
return invokeMethod(getClass(className, type), name, instance, args);
}
public static Field getField(Class<?> clazz, String name) throws Exception {
Field f = clazz.getField(name);
f.setAccessible(true);
return f;
}
public static Field getField(String className, PackageType type, String name) throws Exception {
return getField(getClass(className, type), name);
}
public static Field getField(String className, SubPackageType type, String name) throws Exception {
return getField(getClass(className, type), name);
}
public static Field getDeclaredField(Class<?> clazz, String name) throws Exception {
Field f = clazz.getDeclaredField(name);
f.setAccessible(true);
return f;
}
public static Field getDeclaredField(String className, PackageType type, String name) throws Exception {
return getDeclaredField(getClass(className, type), name);
}
public static Field getDeclaredField(String className, SubPackageType type, String name) throws Exception {
return getDeclaredField(getClass(className, type), name);
}
public static Object getValue(Object instance, String fieldName) throws Exception {
return getField(instance.getClass(), fieldName).get(instance);
}
public static Object getValue(Class<?> clazz, Object instance, String fieldName) throws Exception {
return getField(clazz, fieldName).get(instance);
}
public static Object getValue(String className, PackageType type, Object instance, String fieldName) throws Exception {
return getValue(getClass(className, type), instance, fieldName);
}
public static Object getValue(String className, SubPackageType type, Object instance, String fieldName) throws Exception {
return getValue(getClass(className, type), instance, fieldName);
}
public static Object getDeclaredValue(Object instance, String fieldName) throws Exception {
return getDeclaredField(instance.getClass(), fieldName).get(instance);
}
public static Object getDeclaredValue(Class<?> clazz, Object instance, String fieldName) throws Exception {
return getDeclaredField(clazz, fieldName).get(instance);
}
public static Object getDeclaredValue(String className, PackageType type, Object instance, String fieldName) throws Exception {
return getDeclaredValue(getClass(className, type), instance, fieldName);
}
public static Object getDeclaredValue(String className, SubPackageType type, Object instance, String fieldName) throws Exception {
return getDeclaredValue(getClass(className, type), instance, fieldName);
}
public static void setValue(Object instance, String fieldName, Object fieldValue) throws Exception {
Field f = getField(instance.getClass(), fieldName);
f.set(instance, fieldValue);
}
public static void setValue(Object instance, FieldPair pair) throws Exception {
setValue(instance, pair.getName(), pair.getValue());
}
public static void setValue(Class<?> clazz, Object instance, String fieldName, Object fieldValue) throws Exception {
Field f = getField(clazz, fieldName);
f.set(instance, fieldValue);
}
public static void setValue(Class<?> clazz, Object instance, FieldPair pair) throws Exception {
setValue(clazz, instance, pair.getName(), pair.getValue());
}
public static void setValue(String className, PackageType type, Object instance, String fieldName, Object fieldValue) throws Exception {
setValue(getClass(className, type), instance, fieldName, fieldValue);
}
public static void setValue(String className, PackageType type, Object instance, FieldPair pair) throws Exception {
setValue(className, type, instance, pair.getName(), pair.getValue());
}
public static void setValue(String className, SubPackageType type, Object instance, String fieldName, Object fieldValue) throws Exception {
setValue(getClass(className, type), instance, fieldName, fieldValue);
}
public static void setValue(String className, SubPackageType type, Object instance, FieldPair pair) throws Exception {
setValue(className, type, instance, pair.getName(), pair.getValue());
}
public static void setValues(Object instance, FieldPair... pairs) throws Exception {
for (FieldPair pair : pairs)
setValue(instance, pair);
}
public static void setValues(Class<?> clazz, Object instance, FieldPair... pairs) throws Exception {
for (FieldPair pair : pairs)
setValue(clazz, instance, pair);
}
public static void setValues(String className, PackageType type, Object instance, FieldPair... pairs) throws Exception {
setValues(getClass(className, type), instance, pairs);
}
public static void setValues(String className, SubPackageType type, Object instance, FieldPair... pairs) throws Exception {
setValues(getClass(className, type), instance, pairs);
}
public static void setDeclaredValue(Object instance, String fieldName, Object fieldValue) throws Exception {
Field f = getDeclaredField(instance.getClass(), fieldName);
f.set(instance, fieldValue);
}
public static void setDeclaredValue(Object instance, FieldPair pair) throws Exception {
setDeclaredValue(instance, pair.getName(), pair.getValue());
}
public static void setDeclaredValue(Class<?> clazz, Object instance, String fieldName, Object fieldValue) throws Exception {
Field f = getDeclaredField(clazz, fieldName);
f.set(instance, fieldValue);
}
public static void setDeclaredValue(Class<?> clazz, Object instance, FieldPair pair) throws Exception {
setDeclaredValue(clazz, instance, pair.getName(), pair.getValue());
}
public static void setDeclaredValue(String className, PackageType type, Object instance, String fieldName, Object fieldValue) throws Exception {
setDeclaredValue(getClass(className, type), instance, fieldName, fieldValue);
}
public static void setDeclaredValue(String className, PackageType type, Object instance, FieldPair pair) throws Exception {
setDeclaredValue(className, type, instance, pair.getName(), pair.getValue());
}
public static void setDeclaredValue(String className, SubPackageType type, Object instance, String fieldName, Object fieldValue) throws Exception {
setDeclaredValue(getClass(className, type), instance, fieldName, fieldValue);
}
public static void setDeclaredValue(String className, SubPackageType type, Object instance, FieldPair pair) throws Exception {
setDeclaredValue(className, type, instance, pair.getName(), pair.getValue());
}
public static void setDeclaredValues(Object instance, FieldPair... pairs) throws Exception {
for (FieldPair pair : pairs)
setDeclaredValue(instance, pair);
}
public static void setDeclaredValues(Class<?> clazz, Object instance, FieldPair... pairs) throws Exception {
for (FieldPair pair : pairs)
setDeclaredValue(clazz, instance, pair);
}
public static void setDeclaredValues(String className, PackageType type, Object instance, FieldPair... pairs) throws Exception {
setDeclaredValues(getClass(className, type), instance, pairs);
}
public static void setDeclaredValues(String className, SubPackageType type, Object instance, FieldPair... pairs) throws Exception {
setDeclaredValues(getClass(className, type), instance, pairs);
}
public enum DataType {
BYTE(byte.class, Byte.class),
SHORT(short.class, Short.class),
INTEGER(int.class, Integer.class),
LONG(long.class, Long.class),
CHARACTER(char.class, Character.class),
FLOAT(float.class, Float.class),
DOUBLE(double.class, Double.class),
BOOLEAN(boolean.class, Boolean.class);
private static final Map<Class<?>, DataType> CLASS_MAP = new HashMap<Class<?>, DataType>();
private final Class<?> primitive;
private final Class<?> reference;
static {
for (DataType t : values()) {
CLASS_MAP.put(t.primitive, t);
CLASS_MAP.put(t.reference, t);
}
}
private DataType(Class<?> primitive, Class<?> reference) {
this.primitive = primitive;
this.reference = reference;
}
public Class<?> getPrimitive() {
return this.primitive;
}
public Class<?> getReference() {
return this.reference;
}
public static DataType fromClass(Class<?> c) {
return CLASS_MAP.get(c);
}
public static Class<?> getPrimitive(Class<?> c) {
DataType t = fromClass(c);
return t == null ? c : t.getPrimitive();
}
public static Class<?> getReference(Class<?> c) {
DataType t = fromClass(c);
return t == null ? c : t.getReference();
}
public static Class<?>[] convertToPrimitive(Class<?>[] classes) {
int length = classes == null ? 0 : classes.length;
Class<?>[] types = new Class<?>[length];
for (int i = 0; i < length; i++)
types[i] = getPrimitive(classes[i]);
return types;
}
public static Class<?>[] convertToPrimitive(Object[] objects) {
int length = objects == null ? 0 : objects.length;
Class<?>[] types = new Class<?>[length];
for (int i = 0; i < length; i++)
types[i] = getPrimitive(objects[i].getClass());
return types;
}
public static boolean equalsArray(Class<?>[] a1, Class<?>[] a2) {
if (a1 == null || a2 == null || a1.length != a2.length)
return false;
for (int i = 0; i < a1.length; i++)
if (!a1[i].equals(a2[i]) && !a1[i].isAssignableFrom(a2[i]))
return false;
return true;
}
}
public final class FieldPair {
private final String name;
private final Object value;
public FieldPair(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName() {
return this.name;
}
public Object getValue() {
return this.value;
}
}
public enum PackageType {
MINECRAFT_SERVER("net.minecraft.server." + Bukkit.getServer().getClass().getPackage().getName().substring(23)),
CRAFTBUKKIT(Bukkit.getServer().getClass().getPackage().getName());
private final String name;
private PackageType(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
@Override
public String toString() {
return name;
}
}
public enum SubPackageType {
BLOCK,
CHUNKIO,
COMMAND,
CONVERSATIONS,
ENCHANTMENS,
ENTITY,
EVENT,
GENERATOR,
HELP,
INVENTORY,
MAP,
METADATA,
POTION,
PROJECTILES,
SCHEDULER,
SCOREBOARD,
UPDATER,
UTIL;
private final String name;
private SubPackageType() {
name = PackageType.CRAFTBUKKIT + "." + name().toLowerCase();
}
public String getName() {
return this.name;
}
@Override
public String toString() {
return name;
}
}
public enum PacketType {
HANDSHAKING_IN_SET_PROTOCOL("PacketHandshakingInSetProtocol"),
LOGIN_IN_ENCRYPTION_BEGIN("PacketLoginInEncryptionBegin"),
LOGIN_IN_START("PacketLoginInStart"),
LOGIN_OUT_DISCONNECT("PacketLoginOutDisconnect"),
LOGIN_OUT_ENCRYPTION_BEGIN("PacketLoginOutEncryptionBegin"),
LOGIN_OUT_SUCCESS("PacketLoginOutSuccess"),
PLAY_IN_ABILITIES("PacketPlayInAbilities"),
PLAY_IN_ARM_ANIMATION("PacketPlayInArmAnimation"),
PLAY_IN_BLOCK_DIG("PacketPlayInBlockDig"),
PLAY_IN_BLOCK_PLACE("PacketPlayInBlockPlace"),
PLAY_IN_CHAT("PacketPlayInChat"),
PLAY_IN_CLIENT_COMMAND("PacketPlayInClientCommand"),
PLAY_IN_CLOSE_WINDOW("PacketPlayInCloseWindow"),
PLAY_IN_CUSTOM_PAYLOAD("PacketPlayInCustomPayload"),
PLAY_IN_ENCHANT_ITEM("PacketPlayInEnchantItem"),
PLAY_IN_ENTITY_ACTION("PacketPlayInEntityAction"),
PLAY_IN_FLYING("PacketPlayInFlying"),
PLAY_IN_HELD_ITEM_SLOT("PacketPlayInHeldItemSlot"),
PLAY_IN_KEEP_ALIVE("PacketPlayInKeepAlive"),
PLAY_IN_LOOK("PacketPlayInLook"),
PLAY_IN_POSITION("PacketPlayInPosition"),
PLAY_IN_POSITION_LOOK("PacketPlayInPositionLook"),
PLAY_IN_SET_CREATIVE_SLOT("PacketPlayInSetCreativeSlot "),
PLAY_IN_SETTINGS("PacketPlayInSettings"),
PLAY_IN_STEER_VEHICLE("PacketPlayInSteerVehicle"),
PLAY_IN_TAB_COMPLETE("PacketPlayInTabComplete"),
PLAY_IN_TRANSACTION("PacketPlayInTransaction"),
PLAY_IN_UPDATE_SIGN("PacketPlayInUpdateSign"),
PLAY_IN_USE_ENTITY("PacketPlayInUseEntity"),
PLAY_IN_WINDOW_CLICK("PacketPlayInWindowClick"),
PLAY_OUT_ABILITIES("PacketPlayOutAbilities"),
PLAY_OUT_ANIMATION("PacketPlayOutAnimation"),
PLAY_OUT_ATTACH_ENTITY("PacketPlayOutAttachEntity"),
PLAY_OUT_BED("PacketPlayOutBed"),
PLAY_OUT_BLOCK_ACTION("PacketPlayOutBlockAction"),
PLAY_OUT_BLOCK_BREAK_ANIMATION("PacketPlayOutBlockBreakAnimation"),
PLAY_OUT_BLOCK_CHANGE("PacketPlayOutBlockChange"),
PLAY_OUT_CHAT("PacketPlayOutChat"),
PLAY_OUT_CLOSE_WINDOW("PacketPlayOutCloseWindow"),
PLAY_OUT_COLLECT("PacketPlayOutCollect"),
PLAY_OUT_CRAFT_PROGRESS_BAR("PacketPlayOutCraftProgressBar"),
PLAY_OUT_CUSTOM_PAYLOAD("PacketPlayOutCustomPayload"),
PLAY_OUT_ENTITY("PacketPlayOutEntity"),
PLAY_OUT_ENTITY_DESTROY("PacketPlayOutEntityDestroy"),
PLAY_OUT_ENTITY_EFFECT("PacketPlayOutEntityEffect"),
PLAY_OUT_ENTITY_EQUIPMENT("PacketPlayOutEntityEquipment"),
PLAY_OUT_ENTITY_HEAD_ROTATION("PacketPlayOutEntityHeadRotation"),
PLAY_OUT_ENTITY_LOOK("PacketPlayOutEntityLook"),
PLAY_OUT_ENTITY_METADATA("PacketPlayOutEntityMetadata"),
PLAY_OUT_ENTITY_STATUS("PacketPlayOutEntityStatus"),
PLAY_OUT_ENTITY_TELEPORT("PacketPlayOutEntityTeleport"),
PLAY_OUT_ENTITY_VELOCITY("PacketPlayOutEntityVelocity"),
PLAY_OUT_EXPERIENCE("PacketPlayOutExperience"),
PLAY_OUT_EXPLOSION("PacketPlayOutExplosion"),
PLAY_OUT_GAME_STATE_CHANGE("PacketPlayOutGameStateChange"),
PLAY_OUT_HELD_ITEM_SLOT("PacketPlayOutHeldItemSlot"),
PLAY_OUT_KEEP_ALIVE("PacketPlayOutKeepAlive"),
PLAY_OUT_KICK_DISCONNECT("PacketPlayOutKickDisconnect"),
PLAY_OUT_LOGIN("PacketPlayOutLogin"),
PLAY_OUT_MAP("PacketPlayOutMap"),
PLAY_OUT_MAP_CHUNK("PacketPlayOutMapChunk"),
PLAY_OUT_MAP_CHUNK_BULK("PacketPlayOutMapChunkBulk"),
PLAY_OUT_MULTI_BLOCK_CHANGE("PacketPlayOutMultiBlockChange"),
PLAY_OUT_NAMED_ENTITY_SPAWN("PacketPlayOutNamedEntitySpawn"),
PLAY_OUT_NAMED_SOUND_EFFECT("PacketPlayOutNamedSoundEffect"),
PLAY_OUT_OPEN_SIGN_EDITOR("PacketPlayOutOpenSignEditor"),
PLAY_OUT_OPEN_WINDOW("PacketPlayOutOpenWindow"),
PLAY_OUT_PLAYER_INFO("PacketPlayOutPlayerInfo"),
PLAY_OUT_POSITION("PacketPlayOutPosition"),
PLAY_OUT_REL_ENTITY_MOVE("PacketPlayOutRelEntityMove"),
PLAY_OUT_REL_ENTITY_MOVE_LOOK("PacketPlayOutRelEntityMoveLook"),
PLAY_OUT_REMOVE_ENTITY_EFFECT("PacketPlayOutRemoveEntityEffect"),
PLAY_OUT_RESPAWN("PacketPlayOutRespawn"),
PLAY_OUT_SCOREBOARD_DISPLAY_OBJECTIVE("PacketPlayOutScoreboardDisplayObjective"),
PLAY_OUT_SCOREBOARD_OBJECTIVE("PacketPlayOutScoreboardObjective"),
PLAY_OUT_SCOREBOARD_SCORE("PacketPlayOutScoreboardScore"),
PLAY_OUT_SCOREBOARD_TEAM("PacketPlayOutScoreboardTeam"),
PLAY_OUT_SET_SLOT("PacketPlayOutSetSlot"),
PLAY_OUT_SPAWN_ENTITY("PacketPlayOutSpawnEntity"),
PLAY_OUT_SPAWN_ENTITY_EXPERIENCE_ORB("PacketPlayOutSpawnEntityExperienceOrb"),
PLAY_OUT_SPAWN_ENTITY_LIVING("PacketPlayOutSpawnEntityLiving"),
PLAY_OUT_SPAWN_ENTITY_PAINTING("PacketPlayOutSpawnEntityPainting"),
PLAY_OUT_SPAWN_ENTITY_WEATHER("PacketPlayOutSpawnEntityWeather"),
PLAY_OUT_SPAWN_POSITION("PacketPlayOutSpawnPosition"),
PLAY_OUT_STATISTIC("PacketPlayOutStatistic"),
PLAY_OUT_TAB_COMPLETE("PacketPlayOutTabComplete"),
PLAY_OUT_TILE_ENTITY_DATA("PacketPlayOutTileEntityData"),
PLAY_OUT_TRANSACTION("PacketPlayOutTransaction"),
PLAY_OUT_UPDATE_ATTRIBUTES("PacketPlayOutUpdateAttributes"),
PLAY_OUT_UPDATE_HEALTH("PacketPlayOutUpdateHealth"),
PLAY_OUT_UPDATE_SIGN("PacketPlayOutUpdateSign"),
PLAY_OUT_UPDATE_TIME("PacketPlayOutUpdateTime"),
PLAY_OUT_WINDOW_ITEMS("PacketPlayOutWindowItems"),
PLAY_OUT_WORLD_EVENT("PacketPlayOutWorldEvent"),
PLAY_OUT_WORLD_PARTICLES("PacketPlayOutWorldParticles"),
STATUS_IN_PING("PacketStatusInPing"),
STATUS_IN_START("PacketStatusInStart"),
STATUS_OUT_PONG("PacketStatusOutPong"),
STATUS_OUT_SERVER_INFO("PacketStatusOutServerInfo");
private final String name;
private Class<?> packet;
private PacketType(String name) {
this.name = name;
}
public String getName() {
return this.getName();
}
public Class<?> getPacket() throws Exception {
return packet == null ? packet = ReflectionUtil.getClass(name, PackageType.MINECRAFT_SERVER) : packet;
}
}
}
@@ -0,0 +1,34 @@
package com.lishid.openinv.utils;
import java.io.File;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.utils.Updater.UpdateResult;
public class UpdateManager {
public Updater updater;
public void Initialize(OpenInv plugin, File file) {
updater = new Updater(plugin, 31432, file);
// Create task to update
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() {
@Override
public void run() {
// Check for updates
if (OpenInv.GetCheckForUpdates()) {
UpdateResult result = updater.update();
if (result != UpdateResult.NO_UPDATE) {
if (result == UpdateResult.SUCCESS) {
OpenInv.log("Update found! Downloaded new version.");
OpenInv.log("This behaviour can be disabled in the config.yml");
}
else {
OpenInv.log("Update failed, reason: " + result.toString());
}
}
}
}
}, 0, 20 * 60 * 1000); // Update every once a while
}
}
+412
View File
@@ -0,0 +1,412 @@
/*
* Updater for Bukkit.
*
* This class provides the means to safely and easily update a plugin, or check to see if it is updated using dev.bukkit.org
*/
package com.lishid.openinv.utils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.bukkit.plugin.Plugin;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
/**
* Check dev.bukkit.org to find updates for a given plugin, and download the updates if needed.
* <p/>
* <b>VERY, VERY IMPORTANT</b>: Because there are no standards for adding auto-update toggles in your plugin's config, this system provides NO CHECK WITH YOUR CONFIG to make sure the user has allowed
* auto-updating. <br>
* It is a <b>BUKKIT POLICY</b> that you include a boolean value in your config that prevents the auto-updater from running <b>AT ALL</b>. <br>
* If you fail to include this option in your config, your plugin will be <b>REJECTED</b> when you attempt to submit it to dev.bukkit.org.
* <p/>
* An example of a good configuration option would be something similar to 'auto-update: true' - if this value is set to false you may NOT run the auto-updater. <br>
* If you are unsure about these rules, please read the plugin submission guidelines: http://goo.gl/8iU5l
*
* @author Gravity
* @version 2.0
*/
public class Updater {
private Plugin plugin;
private String versionName;
private String versionLink;
@SuppressWarnings("unused")
private String versionType;
@SuppressWarnings("unused")
private String versionGameVersion;
private boolean announce; // Whether to announce file downloads
private URL url; // Connecting to RSS
private File file; // The plugin's file
private int id = 31432; // Project's Curse ID
// SEE https://dev.bukkit.org/home/servermods-apikey/
private String apiKey = null; // BukkitDev ServerMods API key
private static final String TITLE_VALUE = "name"; // Gets remote file's title
private static final String LINK_VALUE = "downloadUrl"; // Gets remote file's download link
private static final String TYPE_VALUE = "releaseType"; // Gets remote file's release type
private static final String VERSION_VALUE = "gameVersion"; // Gets remote file's build version
private static final String QUERY = "/servermods/files?projectIds="; // Path to GET
private static final String HOST = "https://api.curseforge.com"; // Slugs will be appended to this to get to the project's RSS feed
private static final String[] NO_UPDATE_TAG = { "-DEV", "-PRE", "-SNAPSHOT" }; // If the version number contains one of these, don't update.
private static final int BYTE_SIZE = 1024; // Used for downloading files
private String updateFolder;// The folder that downloads will be placed in
private Updater.UpdateResult result = Updater.UpdateResult.SUCCESS; // Used for determining the outcome of the update process
/**
* Gives the dev the result of the update process. Can be obtained by called getResult().
*/
public enum UpdateResult {
/**
* The updater found an update, and has readied it to be loaded the next time the server restarts/reloads.
*/
SUCCESS,
/**
* The updater did not find an update, and nothing was downloaded.
*/
NO_UPDATE,
/**
* The server administrator has disabled the updating system
*/
DISABLED,
/**
* The updater found an update, but was unable to download it.
*/
FAIL_DOWNLOAD,
/**
* For some reason, the updater was unable to contact dev.bukkit.org to download the file.
*/
FAIL_DBO,
/**
* When running the version check, the file on DBO did not contain the a version in the format 'vVersion' such as 'v1.0'.
*/
FAIL_NOVERSION,
/**
* The id provided by the plugin running the updater was invalid and doesn't exist on DBO.
*/
FAIL_BADID,
/**
* The server administrator has improperly configured their API key in the configuration
*/
FAIL_APIKEY,
/**
* The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded.
*/
UPDATE_AVAILABLE
}
/**
* Initialize the updater
*
* @param plugin The plugin that is checking for an update.
* @param id The dev.bukkit.org id of the project
* @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class.
*/
public Updater(Plugin plugin, int id, File file) {
this.plugin = plugin;
this.file = file;
this.id = id;
this.updateFolder = plugin.getServer().getUpdateFolder();
try {
this.url = new URL(Updater.HOST + Updater.QUERY + id);
}
catch (final MalformedURLException e) {
plugin.getLogger().severe("The project ID provided for updating, " + id + " is invalid.");
this.result = UpdateResult.FAIL_BADID;
e.printStackTrace();
}
}
/**
* Save an update from dev.bukkit.org into the server's update folder.
*/
private void saveFile(File folder, String file, String u) {
if (!folder.exists()) {
folder.mkdir();
}
BufferedInputStream in = null;
FileOutputStream fout = null;
try {
// Download the file
final URL url = new URL(u);
final int fileLength = url.openConnection().getContentLength();
in = new BufferedInputStream(url.openStream());
fout = new FileOutputStream(folder.getAbsolutePath() + "/" + file);
final byte[] data = new byte[Updater.BYTE_SIZE];
int count;
if (this.announce) {
this.plugin.getLogger().info("About to download a new update: " + this.versionName);
}
long downloaded = 0;
while ((count = in.read(data, 0, Updater.BYTE_SIZE)) != -1) {
downloaded += count;
fout.write(data, 0, count);
final int percent = (int) ((downloaded * 100) / fileLength);
if (this.announce && ((percent % 10) == 0)) {
this.plugin.getLogger().info("Downloading update: " + percent + "% of " + fileLength + " bytes.");
}
}
// Just a quick check to make sure we didn't leave any files from last time...
for (final File xFile : new File(this.plugin.getDataFolder().getParent(), this.updateFolder).listFiles()) {
if (xFile.getName().endsWith(".zip")) {
xFile.delete();
}
}
// Check to see if it's a zip file, if it is, unzip it.
final File dFile = new File(folder.getAbsolutePath() + "/" + file);
if (dFile.getName().endsWith(".zip")) {
// Unzip
this.unzip(dFile.getCanonicalPath());
}
if (this.announce) {
this.plugin.getLogger().info("Finished updating.");
}
}
catch (final Exception ex) {
this.plugin.getLogger().warning("The auto-updater tried to download a new update, but was unsuccessful.");
this.result = Updater.UpdateResult.FAIL_DOWNLOAD;
}
finally {
try {
if (in != null) {
in.close();
}
if (fout != null) {
fout.close();
}
}
catch (final Exception ex) {}
}
}
/**
* Part of Zip-File-Extractor, modified by Gravity for use with Bukkit
*/
private void unzip(String file) {
try {
final File fSourceZip = new File(file);
final String zipPath = file.substring(0, file.length() - 4);
ZipFile zipFile = new ZipFile(fSourceZip);
Enumeration<? extends ZipEntry> e = zipFile.entries();
while (e.hasMoreElements()) {
ZipEntry entry = e.nextElement();
File destinationFilePath = new File(zipPath, entry.getName());
destinationFilePath.getParentFile().mkdirs();
if (entry.isDirectory()) {
continue;
}
else {
final BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
int b;
final byte buffer[] = new byte[Updater.BYTE_SIZE];
final FileOutputStream fos = new FileOutputStream(destinationFilePath);
final BufferedOutputStream bos = new BufferedOutputStream(fos, Updater.BYTE_SIZE);
while ((b = bis.read(buffer, 0, Updater.BYTE_SIZE)) != -1) {
bos.write(buffer, 0, b);
}
bos.flush();
bos.close();
bis.close();
final String name = destinationFilePath.getName();
if (name.endsWith(".jar") && this.pluginFile(name)) {
destinationFilePath.renameTo(new File(this.plugin.getDataFolder().getParent(), this.updateFolder + "/" + name));
}
}
entry = null;
destinationFilePath = null;
}
e = null;
zipFile.close();
zipFile = null;
// Move any plugin data folders that were included to the right place, Bukkit won't do this for us.
for (final File dFile : new File(zipPath).listFiles()) {
if (dFile.isDirectory()) {
if (this.pluginFile(dFile.getName())) {
final File oFile = new File(this.plugin.getDataFolder().getParent(), dFile.getName()); // Get current dir
final File[] contents = oFile.listFiles(); // List of existing files in the current dir
for (final File cFile : dFile.listFiles()) // Loop through all the files in the new dir
{
boolean found = false;
for (final File xFile : contents) // Loop through contents to see if it exists
{
if (xFile.getName().equals(cFile.getName())) {
found = true;
break;
}
}
if (!found) {
// Move the new file into the current dir
cFile.renameTo(new File(oFile.getCanonicalFile() + "/" + cFile.getName()));
}
else {
// This file already exists, so we don't need it anymore.
cFile.delete();
}
}
}
}
dFile.delete();
}
new File(zipPath).delete();
fSourceZip.delete();
}
catch (final IOException ex) {
this.plugin.getLogger().warning("The auto-updater tried to unzip a new update file, but was unsuccessful.");
this.result = Updater.UpdateResult.FAIL_DOWNLOAD;
ex.printStackTrace();
}
new File(file).delete();
}
/**
* Check if the name of a jar is one of the plugins currently installed, used for extracting the correct files out of a zip.
*/
private boolean pluginFile(String name) {
for (final File file : new File("plugins").listFiles()) {
if (file.getName().equals(name)) {
return true;
}
}
return false;
}
/**
* Check to see if the program should continue by evaluation whether the plugin is already updated, or shouldn't be updated
*/
private boolean versionCheck(String title) {
final String version = this.plugin.getDescription().getVersion();
if (title.split(" ").length == 2) {
final String remoteVersion = title.split(" ")[1].split(" ")[0]; // Get the newest file's version number
if (this.hasTag(version) || version.equalsIgnoreCase(remoteVersion) || !isNewer(version, remoteVersion)) {
// We already have the latest version, or this build is tagged for no-update
this.result = Updater.UpdateResult.NO_UPDATE;
return false;
}
}
else {
this.plugin.getLogger().warning("File versions should follow the format 'PluginName VERSION'");
this.result = Updater.UpdateResult.FAIL_NOVERSION;
return false;
}
return true;
}
/**
* Evaluate whether the version number is marked showing that it should not be updated by this program
*/
private boolean hasTag(String version) {
for (final String string : Updater.NO_UPDATE_TAG) {
if (version.contains(string)) {
return true;
}
}
return false;
}
private boolean read() {
try {
final URLConnection conn = this.url.openConnection();
conn.setConnectTimeout(5000);
if (this.apiKey != null) {
conn.addRequestProperty("X-API-Key", this.apiKey);
}
conn.addRequestProperty("User-Agent", "Updater");
conn.setDoOutput(true);
final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
final String response = reader.readLine();
final JSONArray array = (JSONArray) JSONValue.parse(response);
if (array.size() == 0) {
this.plugin.getLogger().warning("The updater could not find any files for the project id " + this.id);
this.result = UpdateResult.FAIL_BADID;
return false;
}
this.versionName = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.TITLE_VALUE);
this.versionLink = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.LINK_VALUE);
this.versionType = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.TYPE_VALUE);
this.versionGameVersion = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.VERSION_VALUE);
return true;
}
catch (final IOException e) {
if (e.getMessage().contains("HTTP response code: 403")) {
this.plugin.getLogger().warning("dev.bukkit.org rejected the API key provided in plugins/Updater/config.yml");
this.plugin.getLogger().warning("Please double-check your configuration to ensure it is correct.");
this.result = UpdateResult.FAIL_APIKEY;
}
else {
this.plugin.getLogger().warning("The updater could not contact curse for updating.");
this.result = UpdateResult.FAIL_DBO;
}
e.printStackTrace();
return false;
}
}
private static boolean isNewer(String oldVers, String newVers) {
String s1 = normalisedVersion(oldVers);
String s2 = normalisedVersion(newVers);
int cmp = s1.compareTo(s2);
return cmp < 0;
}
public static String normalisedVersion(String version) {
return normalisedVersion(version, ".", 3);
}
public static String normalisedVersion(String version, String sep, int maxWidth) {
String[] split = Pattern.compile(sep, Pattern.LITERAL).split(version);
StringBuilder sb = new StringBuilder();
for (String s : split) {
sb.append(String.format("%" + maxWidth + 's', s));
}
return sb.toString();
}
public UpdateResult update() {
if (Updater.this.url != null) {
// Obtain the results of the project's file feed
if (Updater.this.read()) {
if (Updater.this.versionCheck(Updater.this.versionName)) {
if (Updater.this.versionLink != null) {
String name = Updater.this.file.getName();
// If it's a zip file, it shouldn't be downloaded as the plugin's name
if (Updater.this.versionLink.endsWith(".zip")) {
final String[] split = Updater.this.versionLink.split("/");
name = split[split.length - 1];
}
Updater.this.saveFile(new File(Updater.this.plugin.getDataFolder().getParent(), Updater.this.updateFolder), name, Updater.this.versionLink);
}
}
}
}
return this.result;
}
}
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff. Show more