mirror of
https://github.com/Jikoo/OpenInv.git
synced 2026-11-04 08:42:05 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb48f6a783 | ||
|
|
d761b6bfe4 | ||
|
|
5b3ba79b82 | ||
|
|
3549431fbc | ||
|
|
17b0cb6efe | ||
|
|
2d7522554c | ||
|
|
131f874329 | ||
|
|
a623af5119 | ||
|
|
8f17bd0237 | ||
|
|
20fb61705d | ||
|
|
c0f513177a | ||
|
|
b3a3947b03 | ||
|
|
c7bab7d451 | ||
|
|
4058463f7f | ||
|
|
21cd1926ae | ||
|
|
2a66b2e81b |
No files matched your search
@@ -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@v4.0.0
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
pull-request-number: "${{ env.PR_NUMBER }}"
|
||||
- name: Merge
|
||||
uses: pascalgn/automerge-action@v0.16.4
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
MERGE_LABELS: "dependencies,java"
|
||||
MERGE_METHOD: "squash"
|
||||
PULL_REQUEST: "${{ env.PR_NUMBER }}"
|
||||
@@ -1,45 +0,0 @@
|
||||
name: OpenInv CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
tags-ignore:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- resource-pack/**
|
||||
# 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: '21'
|
||||
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@v4
|
||||
with:
|
||||
name: dist
|
||||
path: ./target/*.jar
|
||||
- name: Upload API Jar
|
||||
id: upload-api
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: api
|
||||
path: ./api/target/openinvapi*.jar
|
||||
@@ -1,45 +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 build
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: dist
|
||||
|
||||
- name: Package resource pack
|
||||
run: |-
|
||||
pushd resource-pack
|
||||
zip -r ../dist/openinv-legibility-pack.zip .
|
||||
popd
|
||||
|
||||
- name: Create Release
|
||||
id: create-release
|
||||
uses: softprops/action-gh-release@v2.2.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
name: ${{ env.VERSIONED_NAME }}
|
||||
body: ${{ env.GENERATED_CHANGELOG }}
|
||||
draft: true
|
||||
prerelease: false
|
||||
files: ./dist/**
|
||||
@@ -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.2
|
||||
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 }}"
|
||||
@@ -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@v4
|
||||
with:
|
||||
name: pr_number
|
||||
path: pr/
|
||||
@@ -1,24 +0,0 @@
|
||||
name: Resource Pack CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
tags-ignore:
|
||||
- '**'
|
||||
paths:
|
||||
- resource-pack/**
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build resource pack
|
||||
id: upload-resource-pack
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: openinv-legibility-pack
|
||||
path: ./resource-pack/
|
||||
compression-level: 9
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
**/.settings
|
||||
**/.project
|
||||
**/.classpath
|
||||
**/.idea/
|
||||
**.iml
|
||||
**/target/
|
||||
**/bin/
|
||||
**/out/
|
||||
**/dependency-reduced-pom.xml
|
||||
**/pom.xml.versionsBackup
|
||||
@@ -1,57 +1,26 @@
|
||||
## About
|
||||
OpenInv is a [Bukkit plugin](https://dev.bukkit.org/projects/openinv) which allows users to open and edit anyone's inventory or ender chest - online or not!
|
||||
## Goals
|
||||
This fork of OpenInv was created when the lag caused by offline player lookups post-UUID migration became too much to bear. Overall, it does not differ much from the original.
|
||||
|
||||
## Features
|
||||
- **OpenInv**: Open anyone's inventory, even if they're offline.
|
||||
- Read-only mode! Don't grant edit permission.
|
||||
- Cross-world support! Allow access only from the same world.
|
||||
- No duplicate slots! Only armor is accessible when opening self (if allowed at all)!
|
||||
- Drop items as the player! Place items in the dropper slot in the bottom right. Can be disabled via permission!
|
||||
- Allow any item in armor slots! Configurable via permission.
|
||||
- **OpenEnder**: Open anyone's ender chest, even if they're offline.
|
||||
- Allow access only to own ender chest! Don't grant permission to open others.
|
||||
- Read-only mode! Don't grant edit permission.
|
||||
- Cross-world support! Allow access only from the same world.
|
||||
- **SilentContainer**: Open containers without displaying an animation or making sound.
|
||||
- **AnyContainer**: Open containers, even if blocked by ocelots or blocks.
|
||||
### Changes of Note
|
||||
- Removed updater
|
||||
- Removed wand
|
||||
|
||||
## Commands
|
||||
See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Commands).
|
||||
## Previous Versions
|
||||
Any version of Minecraft this fork has been existent for should have a [release](https://github.com/Jikoo/OpenInv/releases) tagged. Features are not backported, however.
|
||||
|
||||
## Permissions
|
||||
See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Permissions)
|
||||
|
||||
## 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>
|
||||
## License
|
||||
```
|
||||
Copyright (C) 2011-2014 lishid. All rights reserved.
|
||||
|
||||
### 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`
|
||||
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.
|
||||
|
||||
To compile just the API, execute Maven as usual:
|
||||
`mvn clean package`
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
To compile the full plugin, use the provided profile with `-P all`:
|
||||
`mvn clean package -am -P all`
|
||||
For more information, check out the [official Maven guide](http://maven.apache.org/guides/introduction/introduction-to-profiles.html).
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
```
|
||||
@@ -1,72 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>5.1.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvtogglepersist</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>OITogglePersist</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-final-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.parent.build.directory}</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<includes>
|
||||
<include>${project.build.finalName}.jar</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
</project>
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
package com.github.jikoo.openinv.togglepersist;
|
||||
|
||||
import com.google.errorprone.annotations.Keep;
|
||||
import com.lishid.openinv.event.PlayerToggledEvent;
|
||||
import com.lishid.openinv.util.setting.PlayerToggle;
|
||||
import com.lishid.openinv.util.setting.PlayerToggles;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class TogglePersist extends JavaPlugin implements Listener {
|
||||
|
||||
private final Map<UUID, Set<String>> enabledToggles = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getServer().getPluginManager().registerEvents(this, this);
|
||||
|
||||
File file = new File(getDataFolder(), "toggles.yml");
|
||||
|
||||
// If there's no save file, there's nothing to load.
|
||||
if (!file.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Configuration loaded = YamlConfiguration.loadConfiguration(file);
|
||||
|
||||
// For each toggle, enable loaded players.
|
||||
for (String toggleName : loaded.getKeys(false)) {
|
||||
PlayerToggle toggle = PlayerToggles.get(toggleName);
|
||||
// Ensure toggle exists.
|
||||
if (toggle == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (String idString : loaded.getStringList(toggleName)) {
|
||||
// Ensure valid UUID.
|
||||
UUID uuid;
|
||||
try {
|
||||
uuid = UUID.fromString(idString);
|
||||
} catch (IllegalArgumentException e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Track that toggle is enabled.
|
||||
set(uuid, toggleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void set(UUID playerId, String toggleName) {
|
||||
enabledToggles.compute(playerId, (uuid, toggles) -> {
|
||||
if (toggles == null) {
|
||||
toggles = new HashSet<>();
|
||||
}
|
||||
toggles.add(toggleName);
|
||||
return toggles;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
Map<String, List<String>> converted = getSaveData();
|
||||
|
||||
YamlConfiguration data = new YamlConfiguration();
|
||||
for (Map.Entry<String, List<String>> playerToggle : converted.entrySet()) {
|
||||
data.set(playerToggle.getKey(), playerToggle.getValue());
|
||||
}
|
||||
|
||||
File file = new File(getDataFolder(), "toggles.yml");
|
||||
try {
|
||||
data.save(file);
|
||||
} catch (IOException e) {
|
||||
getLogger().log(Level.SEVERE, "Unable to save player toggle states", e);
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull Map<String, List<String>> getSaveData() {
|
||||
Map<String, List<String>> converted = new HashMap<>();
|
||||
|
||||
for (Map.Entry<UUID, Set<String>> playerToggles : enabledToggles.entrySet()) {
|
||||
String idString = playerToggles.getKey().toString();
|
||||
for (String toggleName : playerToggles.getValue()) {
|
||||
// Add player ID to listing for each enabled toggle.
|
||||
converted.compute(toggleName, (name, ids) -> {
|
||||
if (ids == null) {
|
||||
ids = new ArrayList<>();
|
||||
}
|
||||
ids.add(idString);
|
||||
return ids;
|
||||
});
|
||||
}
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler
|
||||
private void onPlayerJoin(@NotNull PlayerJoinEvent event) {
|
||||
UUID playerId = event.getPlayer().getUniqueId();
|
||||
Set<String> toggleNames = enabledToggles.get(playerId);
|
||||
|
||||
if (toggleNames == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String toggleName : toggleNames) {
|
||||
PlayerToggle toggle = PlayerToggles.get(toggleName);
|
||||
if (toggle != null) {
|
||||
toggle.set(playerId, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler
|
||||
private void onToggleSet(@NotNull PlayerToggledEvent event) {
|
||||
if (event.isEnabled()) {
|
||||
set(event.getPlayerId(), event.getToggle().getName());
|
||||
} else {
|
||||
enabledToggles.computeIfPresent(event.getPlayerId(), (uuid, toggles) -> {
|
||||
toggles.remove(event.getToggle().getName());
|
||||
return toggles.isEmpty() ? null : toggles;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
name: OITogglePersist
|
||||
main: com.github.jikoo.openinv.togglepersist.TogglePersist
|
||||
version: ${project.version}
|
||||
author: Jikoo
|
||||
description: An OpenInv addon allowing /anycontainer and /silentcontainer to persist across sessions.
|
||||
api-version: "1.20"
|
||||
depend: [ OpenInv ]
|
||||
-49
@@ -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>5.1.6</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,185 +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.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 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);
|
||||
|
||||
/**
|
||||
* 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,56 +0,0 @@
|
||||
package com.lishid.openinv.event;
|
||||
|
||||
import com.google.errorprone.annotations.RestrictedApi;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Event fired before OpenInv saves a player's data when closing an {@link ISpecialInventory}.
|
||||
*/
|
||||
public class OpenPlayerSaveEvent extends PlayerSaveEvent {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
private final ISpecialInventory inventory;
|
||||
|
||||
/**
|
||||
* Construct a new {@code OpenPlayerSaveEvent}.
|
||||
*
|
||||
* <p>The constructor is not considered part of the API, and may be subject to change.</p>
|
||||
*
|
||||
* @param player the player to be saved
|
||||
* @param inventory the {@link ISpecialInventory} being closed
|
||||
*/
|
||||
@RestrictedApi(
|
||||
explanation = "Constructor is not considered part of the API and may be subject to change.",
|
||||
link = "",
|
||||
allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java")
|
||||
@ApiStatus.Internal
|
||||
OpenPlayerSaveEvent(@NotNull Player player, @NotNull ISpecialInventory inventory) {
|
||||
super(player);
|
||||
this.inventory = inventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link ISpecialInventory} that triggered the save by being closed.
|
||||
*
|
||||
* @return the special inventory
|
||||
*/
|
||||
public @NotNull ISpecialInventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package com.lishid.openinv.event;
|
||||
|
||||
|
||||
import com.google.errorprone.annotations.RestrictedApi;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Event fired before a {@link Player} loaded via OpenInv is saved.
|
||||
*/
|
||||
public class PlayerSaveEvent extends PlayerEvent implements Cancellable {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
private boolean cancelled = false;
|
||||
|
||||
/**
|
||||
* Construct a new {@code PlayerSaveEvent}.
|
||||
*
|
||||
* <p>The constructor is not considered part of the API, and may be subject to change.</p>
|
||||
*
|
||||
* @param player the player to be saved
|
||||
*/
|
||||
@RestrictedApi(
|
||||
explanation = "Constructor is not considered part of the API and may be subject to change.",
|
||||
link = "",
|
||||
allowedOnPath = ".*/com/lishid/openinv/event/(OpenPlayerSaveEvent|OpenEvents).java")
|
||||
@ApiStatus.Internal
|
||||
PlayerSaveEvent(@NotNull Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,71 +0,0 @@
|
||||
package com.lishid.openinv.event;
|
||||
|
||||
import com.google.errorprone.annotations.RestrictedApi;
|
||||
import com.lishid.openinv.util.setting.PlayerToggle;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Event fired after OpenInv modifies a toggleable setting for a player.
|
||||
*/
|
||||
public class PlayerToggledEvent extends Event {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
private final @NotNull PlayerToggle toggle;
|
||||
private final @NotNull UUID uuid;
|
||||
private final boolean enabled;
|
||||
|
||||
@RestrictedApi(
|
||||
explanation = "Constructor is not considered part of the API and may be subject to change.",
|
||||
link = "",
|
||||
allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java")
|
||||
@ApiStatus.Internal
|
||||
PlayerToggledEvent(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean enabled) {
|
||||
this.toggle = toggle;
|
||||
this.uuid = uuid;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link PlayerToggle} affected.
|
||||
*
|
||||
* @return the toggle
|
||||
*/
|
||||
public @NotNull PlayerToggle getToggle() {
|
||||
return toggle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link UUID} of the player whose setting was changed.
|
||||
*
|
||||
* @return the player ID
|
||||
*/
|
||||
public @NotNull UUID getPlayerId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the toggle is enabled.
|
||||
*
|
||||
* @return true if the toggle is enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,122 +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.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.EnderChest;
|
||||
import org.bukkit.block.data.Directional;
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
boolean isAnyContainerNeeded(@NotNull Block block);
|
||||
|
||||
/**
|
||||
* Check if a shulker box block cannot be opened under ordinary circumstances.
|
||||
*
|
||||
* @param shulkerBox the shulker box block
|
||||
* @return whether the container is blocked
|
||||
*/
|
||||
default boolean isShulkerBlocked(@NotNull Block shulkerBox) {
|
||||
Directional directional = (Directional) shulkerBox.getBlockData();
|
||||
BlockFace facing = directional.getFacing();
|
||||
// Construct a new 1-block bounding box at the origin.
|
||||
BoundingBox box = new BoundingBox(0, 0, 0, 1, 1, 1);
|
||||
// Expand the box in the direction the shulker will open.
|
||||
box.expand(facing, 0.5);
|
||||
// Move the box away from the origin by a block so only the expansion intersects with a box around the origin.
|
||||
box.shift(facing.getOppositeFace().getDirection());
|
||||
// Check if the relative block's collision shape (which will be at the origin) intersects with the expanded box.
|
||||
return shulkerBox.getRelative(facing).getCollisionShape().overlaps(box);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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), Cat.class::isInstance).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
boolean isAnySilentContainer(@NotNull Block block);
|
||||
|
||||
/**
|
||||
* 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,72 +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.event.inventory.InventoryType;
|
||||
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();
|
||||
|
||||
/**
|
||||
* Get the {@link InventoryType} corresponding to this {@code ISpecialInventory}.
|
||||
*
|
||||
* @return the type of Bukkit inventory
|
||||
*/
|
||||
@NotNull InventoryType getBukkitType();
|
||||
|
||||
/**
|
||||
* 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,104 +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.google.errorprone.annotations.RestrictedApi;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public final class InventoryAccess {
|
||||
|
||||
private static @Nullable BiFunction<Inventory, Class<? extends ISpecialInventory>, ISpecialInventory> provider;
|
||||
|
||||
public static boolean isUsable() {
|
||||
return provider != 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 provider == null ? null : (ISpecialPlayerInventory) provider.apply(inventory, ISpecialPlayerInventory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 provider == null ? null : (ISpecialEnderChest) provider.apply(inventory, ISpecialEnderChest.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@link ISpecialInventory} 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 ISpecialInventory getInventory(@NotNull Inventory inventory) {
|
||||
return provider == null ? null : provider.apply(inventory, ISpecialInventory.class);
|
||||
}
|
||||
|
||||
@RestrictedApi(
|
||||
explanation = "Not part of the API.",
|
||||
link = "",
|
||||
allowedOnPath = ".*/com/lishid/openinv/util/InternalAccessor.java")
|
||||
@ApiStatus.Internal
|
||||
static void setProvider(@Nullable BiFunction<Inventory, Class<? extends ISpecialInventory>, ISpecialInventory> provider) {
|
||||
InventoryAccess.provider = provider;
|
||||
}
|
||||
|
||||
private InventoryAccess() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.lishid.openinv.util.setting;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A per-player setting that may be enabled or disabled.
|
||||
*/
|
||||
public interface PlayerToggle {
|
||||
|
||||
/**
|
||||
* Get the name of the setting.
|
||||
*
|
||||
* @return the setting name
|
||||
*/
|
||||
@NotNull String getName();
|
||||
|
||||
/**
|
||||
* Get the state of the toggle for a particular player ID.
|
||||
*
|
||||
* @param uuid the player ID
|
||||
* @return true if the setting is enabled
|
||||
*/
|
||||
boolean is(@NotNull UUID uuid);
|
||||
|
||||
/**
|
||||
* Set the state of the toggle for a particular player ID.
|
||||
*
|
||||
* @param uuid the player ID
|
||||
* @param enabled whether the setting is enabled
|
||||
* @return true if the setting changed as a result of being set
|
||||
*/
|
||||
boolean set(@NotNull UUID uuid, boolean enabled);
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package com.lishid.openinv.util.setting;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnmodifiableView;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Utility class containing all of OpenInv's {@link PlayerToggle PlayerToggles}.
|
||||
*/
|
||||
public final class PlayerToggles {
|
||||
|
||||
private static final Map<String, PlayerToggle> TOGGLES = new HashMap<>();
|
||||
private static final PlayerToggle ANY = add(new MemoryToggle("AnyContainer"));
|
||||
private static final PlayerToggle SILENT = add(new MemoryToggle("SilentContainer"));
|
||||
|
||||
/**
|
||||
* Get the AnyContainer toggle.
|
||||
*
|
||||
* @return the AnyContainer toggle
|
||||
*/
|
||||
public static @NotNull PlayerToggle any() {
|
||||
return ANY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SilentContainer toggle.
|
||||
*
|
||||
* @return the SilentContainer toggle
|
||||
*/
|
||||
public static @NotNull PlayerToggle silent() {
|
||||
return SILENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a toggle by name.
|
||||
*
|
||||
* @param toggleName the name of the toggle
|
||||
* @return the toggle, or null if no such toggle exists.
|
||||
*/
|
||||
public static @Nullable PlayerToggle get(@NotNull String toggleName) {
|
||||
PlayerToggle toggle = TOGGLES.get(toggleName);
|
||||
if (toggle == null) {
|
||||
toggle = TOGGLES.get(toggleName.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
return toggle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmodifable view of all toggles available.
|
||||
*
|
||||
* @return a view of all toggles available
|
||||
*/
|
||||
public static @UnmodifiableView @NotNull Collection<PlayerToggle> get() {
|
||||
return Collections.unmodifiableCollection(TOGGLES.values());
|
||||
}
|
||||
|
||||
private static @NotNull PlayerToggle add(@NotNull PlayerToggle toggle) {
|
||||
TOGGLES.put(toggle.getName().toLowerCase(Locale.ENGLISH), toggle);
|
||||
return toggle;
|
||||
}
|
||||
|
||||
private PlayerToggles() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
private static class MemoryToggle implements PlayerToggle {
|
||||
|
||||
private final @NotNull Set<UUID> enabled;
|
||||
private final @NotNull String name;
|
||||
|
||||
private MemoryToggle(@NotNull String name) {
|
||||
enabled = new HashSet<>();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean is(@NotNull UUID uuid) {
|
||||
return enabled.contains(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean set(@NotNull UUID uuid, boolean enabled) {
|
||||
if (enabled) {
|
||||
return this.enabled.add(uuid);
|
||||
} else {
|
||||
return this.enabled.remove(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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>5.1.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvcommon</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.lishid.openinv.event;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.util.setting.PlayerToggle;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Construct and call events.
|
||||
*/
|
||||
public final class OpenEvents {
|
||||
|
||||
public static boolean saveCancelled(@NotNull Player player) {
|
||||
return call(new PlayerSaveEvent(player));
|
||||
}
|
||||
|
||||
public static boolean saveCancelled(@NotNull ISpecialInventory inventory) {
|
||||
return call(new OpenPlayerSaveEvent((Player) inventory.getPlayer(), inventory));
|
||||
}
|
||||
|
||||
public static void notifyPlayerToggle(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean state) {
|
||||
Bukkit.getPluginManager().callEvent(new PlayerToggledEvent(toggle, uuid, state));
|
||||
}
|
||||
|
||||
private static <T extends Event & Cancellable> boolean call(T event) {
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
return event.isCancelled();
|
||||
}
|
||||
|
||||
private OpenEvents() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface Accessor {
|
||||
|
||||
@NotNull
|
||||
PlayerManager getPlayerManager();
|
||||
|
||||
@NotNull IAnySilentContainer getAnySilentContainer();
|
||||
|
||||
@NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player);
|
||||
|
||||
@NotNull ISpecialEnderChest createEnderChest(@NotNull Player player);
|
||||
|
||||
<T extends ISpecialInventory> @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class<T> clazz);
|
||||
|
||||
void reload(@NotNull ConfigurationSection config);
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
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.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public abstract class AnySilentContainerBase implements IAnySilentContainer {
|
||||
|
||||
private static final @Nullable Method BLOCK_GET_STATE_BOOLEAN;
|
||||
|
||||
static {
|
||||
@Nullable Method getState;
|
||||
try {
|
||||
//noinspection JavaReflectionMemberAccess
|
||||
getState = Block.class.getMethod("getState", boolean.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
getState = null;
|
||||
}
|
||||
BLOCK_GET_STATE_BOOLEAN = getState;
|
||||
}
|
||||
|
||||
private static BlockState getBlockState(Block block) {
|
||||
// Paper: Get state without snapshotting.
|
||||
if (BLOCK_GET_STATE_BOOLEAN != null) {
|
||||
try {
|
||||
return (BlockState) BLOCK_GET_STATE_BOOLEAN.invoke(block, false);
|
||||
} catch (ReflectiveOperationException ignored) {
|
||||
// If we encounter an issue, fall through to regular snapshotting method.
|
||||
}
|
||||
}
|
||||
return block.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnyContainerNeeded(@NotNull Block block) {
|
||||
BlockState blockState = getBlockState(block);
|
||||
|
||||
// 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 half a block clear in the direction they open.
|
||||
if (blockState instanceof ShulkerBox) {
|
||||
return isShulkerBlocked(block);
|
||||
}
|
||||
|
||||
if (!(blockState instanceof org.bukkit.block.Chest)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isChestBlocked(block)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockData blockData = block.getBlockData();
|
||||
if (!(blockData instanceof Chest chest) || chest.getType() == Chest.Type.SINGLE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BlockFace relativeFace = switch (chest.getFacing()) {
|
||||
case NORTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.WEST : BlockFace.EAST;
|
||||
case EAST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.NORTH : BlockFace.SOUTH;
|
||||
case SOUTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.EAST : BlockFace.WEST;
|
||||
case WEST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.SOUTH : BlockFace.NORTH;
|
||||
default -> BlockFace.SELF;
|
||||
};
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnySilentContainer(@NotNull Block block) {
|
||||
return isAnySilentContainer(getBlockState(block));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
public interface InternalOwned<T> {
|
||||
|
||||
T getOwnerHandle();
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public enum InventoryViewTitle {
|
||||
|
||||
PLAYER_INVENTORY("container.player", "'s Inventory"),
|
||||
ENDER_CHEST("container.enderchest", "'s Ender Chest");
|
||||
|
||||
private final String localizationKey;
|
||||
private final String defaultSuffix;
|
||||
|
||||
InventoryViewTitle(String localizationKey, String defaultSuffix) {
|
||||
this.localizationKey = localizationKey;
|
||||
this.defaultSuffix = defaultSuffix;
|
||||
}
|
||||
|
||||
public @NotNull String getTitle(
|
||||
@NotNull LanguageManager lang,
|
||||
@NotNull Player viewer,
|
||||
@NotNull ISpecialInventory inventory) {
|
||||
HumanEntity owner = inventory.getPlayer();
|
||||
|
||||
String localTitle = lang.getLocalizedMessage(
|
||||
viewer,
|
||||
localizationKey,
|
||||
new Replacement("%player%", owner.getName()));
|
||||
return Objects.requireNonNullElseGet(localTitle, () -> owner.getName() + defaultSuffix);
|
||||
}
|
||||
|
||||
public static @Nullable InventoryViewTitle of(@NotNull ISpecialInventory inventory) {
|
||||
if (inventory instanceof ISpecialPlayerInventory) {
|
||||
return PLAYER_INVENTORY;
|
||||
} else if (inventory instanceof ISpecialEnderChest) {
|
||||
return ENDER_CHEST;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +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 PlayerManager {
|
||||
|
||||
/**
|
||||
* 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, boolean viewOnly);
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package com.lishid.openinv.internal;
|
||||
|
||||
public interface ViewOnly {
|
||||
}
|
||||
@@ -1,60 +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 {
|
||||
|
||||
INVENTORY_OPEN_SELF("inventory.open.self"),
|
||||
INVENTORY_OPEN_OTHER("inventory.open.other"),
|
||||
INVENTORY_EDIT_SELF("inventory.edit.self"),
|
||||
INVENTORY_EDIT_OTHER("inventory.edit.other"),
|
||||
INVENTORY_SLOT_HEAD_ANY("inventory.slot.head.any"),
|
||||
INVENTORY_SLOT_CHEST_ANY("inventory.slot.chest.any"),
|
||||
INVENTORY_SLOT_LEGS_ANY("inventory.slot.legs.any"),
|
||||
INVENTORY_SLOT_FEET_ANY("inventory.slot.feet.any"),
|
||||
INVENTORY_SLOT_DROP("inventory.slot.drop"),
|
||||
|
||||
ENDERCHEST_OPEN_SELF("enderchest.open.self"),
|
||||
ENDERCHEST_OPEN_OTHER("enderchest.open.other"),
|
||||
ENDERCHEST_EDIT_SELF("enderchest.edit.self"),
|
||||
ENDERCHEST_EDIT_OTHER("enderchest.edit.other"),
|
||||
|
||||
ACCESS_CROSSWORLD("access.crossworld"),
|
||||
ACCESS_OFFLINE("access.offline"),
|
||||
ACCESS_ONLINE("access.online"),
|
||||
|
||||
SPECTATE_CLICK("spectate.click"),
|
||||
|
||||
CONTAINER_ANY("container.any"),
|
||||
CONTAINER_SILENT("container.silent"),
|
||||
SEARCH_INVENTORY("search.inventory"),
|
||||
SEARCH_CONTAINER("search.container");
|
||||
|
||||
private final String permission;
|
||||
|
||||
Permissions(String permission) {
|
||||
this.permission = "openinv." + permission;
|
||||
}
|
||||
|
||||
public boolean hasPermission(@NotNull Permissible permissible) {
|
||||
return permissible.hasPermission(permission);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2021 lishid. All rights reserved.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* A utility for making reflection easier.
|
||||
*/
|
||||
public final class 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()) {
|
||||
if (fieldType.isAssignableFrom(field.getType())) {
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
if (holderType.getSuperclass() != null) {
|
||||
return grabFieldByType(fieldType, holderType.getSuperclass());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ReflectionHelper() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,319 +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 net.md_5.bungee.api.ChatMessageType;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A simple language manager supporting both custom and bundled languages.
|
||||
*
|
||||
* @author Jikoo
|
||||
*/
|
||||
public class LanguageManager {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final File folder;
|
||||
private final String defaultLocale;
|
||||
private final Map<String, YamlConfiguration> locales;
|
||||
|
||||
public LanguageManager(@NotNull Plugin plugin, @NotNull String defaultLocale) {
|
||||
this.plugin = plugin;
|
||||
this.defaultLocale = defaultLocale;
|
||||
this.locales = new HashMap<>();
|
||||
this.folder = new File(plugin.getDataFolder(), "locale");
|
||||
|
||||
if (!folder.exists() && !folder.mkdirs()) {
|
||||
plugin.getLogger().warning(() -> "Unable to create " + folder.getPath() + "! Languages may not be editable.");
|
||||
}
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
this.locales.clear();
|
||||
getOrLoadLocale(defaultLocale);
|
||||
}
|
||||
|
||||
private @NotNull YamlConfiguration getOrLoadLocale(@NotNull String locale) {
|
||||
YamlConfiguration loaded = locales.get(locale);
|
||||
if (loaded != null) {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
LangLocation lang = bestMatch(locale, null);
|
||||
|
||||
// If a parent was a better match, check if it is already loaded.
|
||||
if (!locale.equals(lang.locale)) {
|
||||
loaded = locales.get(lang.locale);
|
||||
if (loaded != null) {
|
||||
locales.put(locale, loaded);
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
|
||||
// Load locale config from disk and bundled locale defaults.
|
||||
YamlConfiguration localeConfig = loadLocale(lang);
|
||||
|
||||
// If the locale is not the default locale, also handle any missing translations from the default locale.
|
||||
if (!locale.equals(defaultLocale)) {
|
||||
addTranslationFallthrough(lang, localeConfig);
|
||||
|
||||
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 " + lang.locale + ".yml! Check the guess section!");
|
||||
}
|
||||
}
|
||||
|
||||
locales.put(locale, localeConfig);
|
||||
locales.put(lang.locale, localeConfig);
|
||||
|
||||
return localeConfig;
|
||||
}
|
||||
|
||||
private @NotNull LangLocation bestMatch(@NotNull String locale, @Nullable LangLocation initial) {
|
||||
File file = new File(folder, locale + ".yml");
|
||||
InputStream bundled = plugin.getResource("locale/" + locale + ".yml");
|
||||
|
||||
if (file.exists() || bundled != null) {
|
||||
return new LangLocation(locale, file, bundled);
|
||||
}
|
||||
|
||||
if (initial == null) {
|
||||
initial = new LangLocation(locale, file, null);
|
||||
}
|
||||
|
||||
int lastSeparator = locale.lastIndexOf('_');
|
||||
|
||||
// Must be at least some content before separator.
|
||||
if (lastSeparator < 1) {
|
||||
return initial;
|
||||
}
|
||||
|
||||
return bestMatch(locale.substring(0, lastSeparator), initial);
|
||||
}
|
||||
|
||||
private @NotNull YamlConfiguration loadLocale(@NotNull LangLocation lang) {
|
||||
YamlConfiguration localeConfigDefaults;
|
||||
if (lang.bundled == null) {
|
||||
localeConfigDefaults = new YamlConfiguration();
|
||||
} else {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(lang.bundled, StandardCharsets.UTF_8))) {
|
||||
localeConfigDefaults = YamlConfiguration.loadConfiguration(reader);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + lang.locale + ".yml");
|
||||
localeConfigDefaults = new YamlConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
if (!lang.file.exists()) {
|
||||
// If the file does not exist on disk, save bundled defaults.
|
||||
try {
|
||||
localeConfigDefaults.save(lang.file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml");
|
||||
}
|
||||
// Return loaded bundled locale.
|
||||
return localeConfigDefaults;
|
||||
}
|
||||
|
||||
// If the file does exist on disk, load it.
|
||||
YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(lang.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 " + lang.locale + ".yml: " + String.join(", ", newKeys));
|
||||
|
||||
// Write new keys to disk.
|
||||
try {
|
||||
localeConfig.save(lang.file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml");
|
||||
}
|
||||
|
||||
return localeConfig;
|
||||
}
|
||||
|
||||
private void addTranslationFallthrough(@NotNull LangLocation location, @NotNull YamlConfiguration localeConfig) {
|
||||
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(location.file);
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + location.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(Locale.ENGLISH)).getString(key);
|
||||
if (value == null || value.isBlank()) {
|
||||
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;
|
||||
}
|
||||
|
||||
public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) {
|
||||
return getValue(key, getLocale(sender));
|
||||
}
|
||||
|
||||
public @Nullable String getLocalizedMessage(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull String key,
|
||||
Replacement @NotNull ... replacements) {
|
||||
return getValue(key, getLocale(sender), replacements);
|
||||
}
|
||||
|
||||
private @NotNull String getLocale(@NotNull CommandSender sender) {
|
||||
if (sender instanceof Player) {
|
||||
return ((Player) sender).getLocale();
|
||||
} else {
|
||||
return plugin.getConfig().getString("settings.locale", "en");
|
||||
}
|
||||
}
|
||||
|
||||
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.fromLegacy(message));
|
||||
}
|
||||
|
||||
private record LangLocation(@NotNull String locale, @NotNull File file, @Nullable InputStream bundled) {}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
File renamed without changes.
@@ -1,77 +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>5.1.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_21_R2</artifactId>
|
||||
<name>OpenInvAdapter1_21_R2</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.system>21</maven.compiler.system>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<spigot.version>1.21.3-R0.1-SNAPSHOT</spigot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>${spigot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>spigot</artifactId>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<scope>provided</scope>
|
||||
<version>${spigot.version}</version>
|
||||
<classifier>remapped-mojang</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvcommon</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>specialsource-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2;
|
||||
|
||||
import com.lishid.openinv.internal.Accessor;
|
||||
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.internal.v1_21_R2.container.AnySilentContainer;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class InternalAccessor implements Accessor {
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private final @NotNull PlayerManager manager;
|
||||
private final @NotNull AnySilentContainer anySilentContainer;
|
||||
|
||||
public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
|
||||
this.logger = logger;
|
||||
manager = new PlayerManager(logger);
|
||||
anySilentContainer = new AnySilentContainer(logger, lang);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PlayerManager getPlayerManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IAnySilentContainer getAnySilentContainer() {
|
||||
return anySilentContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) {
|
||||
return new OpenInventory(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) {
|
||||
return new OpenEnderChest(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ISpecialInventory> @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class<T> clazz) {
|
||||
if (!(bukkitInventory instanceof CraftInventory craftInventory)) {
|
||||
return null;
|
||||
}
|
||||
Container container = craftInventory.getInventory();
|
||||
if (clazz.isInstance(container)) {
|
||||
return clazz.cast(container);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(@NotNull ConfigurationSection config) {
|
||||
ConfigurationSection placeholders = config.getConfigurationSection("placeholders");
|
||||
if (placeholders != null) {
|
||||
try {
|
||||
Placeholders.load(placeholders);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-210
@@ -1,210 +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_21_R2.container;
|
||||
|
||||
import com.lishid.openinv.internal.AnySilentContainerBase;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenChestMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager;
|
||||
import com.lishid.openinv.util.ReflectionHelper;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
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.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.state.BlockState;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
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 extends AnySilentContainerBase {
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private final @NotNull LanguageManager lang;
|
||||
private @Nullable Field serverPlayerGameModeGameType;
|
||||
|
||||
public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) {
|
||||
this.logger = logger;
|
||||
this.lang = lang;
|
||||
try {
|
||||
try {
|
||||
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
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.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 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 = PlayerManager.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 = OpenChestMenu.getChestMenuType(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) {
|
||||
lang.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) {
|
||||
lang.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 = PlayerManager.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) {
|
||||
logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-196
@@ -1,196 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenEnderChestMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedItemContents;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.StackedContentsCompatible;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OpenEnderChest implements Container, StackedContentsCompatible, InternalOwned<ServerPlayer>,
|
||||
ISpecialEnderChest {
|
||||
|
||||
private CraftInventory inventory;
|
||||
private @NotNull ServerPlayer owner;
|
||||
private NonNullList<ItemStack> items;
|
||||
private int maxStack = 64;
|
||||
private final List<HumanEntity> transaction = new ArrayList<>();
|
||||
|
||||
public OpenEnderChest(@NotNull org.bukkit.entity.Player player) {
|
||||
this.owner = PlayerManager.getHandle(player);
|
||||
this.items = owner.getEnderChestInventory().items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ServerPlayer getOwnerHandle() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() {
|
||||
if (inventory == null) {
|
||||
inventory = new CraftInventory(this) {
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return InventoryType.ENDER_CHEST;
|
||||
}
|
||||
};
|
||||
}
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
owner = PlayerManager.getHandle(player);
|
||||
NonNullList<ItemStack> activeItems = owner.getEnderChestInventory().items;
|
||||
|
||||
// Guard against size changing. Theoretically on Purpur all row variations still have 6 rows internally.
|
||||
int max = Math.min(items.size(), activeItems.size());
|
||||
for (int index = 0; index < max; ++index) {
|
||||
activeItems.set(index, items.get(index));
|
||||
}
|
||||
|
||||
items = activeItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return items.stream().allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return index >= 0 && index < items.size() ? items.get(index) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int index, int amount) {
|
||||
ItemStack itemstack = ContainerHelper.removeItem(items, index, amount);
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int index) {
|
||||
return index >= 0 && index < items.size() ? items.set(index, ItemStack.EMPTY) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack itemStack) {
|
||||
if (index >= 0 && index < items.size()) {
|
||||
items.set(index, itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return maxStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
this.owner.getEnderChestInventory().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity craftHumanEntity) {
|
||||
transaction.add(craftHumanEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity craftHumanEntity) {
|
||||
transaction.remove(craftHumanEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Player getOwner() {
|
||||
return getPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int size) {
|
||||
maxStack = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
items.clear();
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedItemContents stackedContents) {
|
||||
for (ItemStack itemstack : items) {
|
||||
stackedContents.accountStack(itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
public Component getTitle() {
|
||||
return Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName())
|
||||
.append(Component.translatable("container.enderchest"))
|
||||
.append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName()));
|
||||
}
|
||||
|
||||
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-364
@@ -1,364 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenPlayerInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.menu.OpenInventoryMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.Content;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCrafting;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCraftingResult;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentCursor;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentDrop;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentEquipment;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentList;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentOffHand;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentViewOnly;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly;
|
||||
import com.lishid.openinv.internal.v1_21_R2.player.PlayerManager;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OpenInventory implements Container, InternalOwned<ServerPlayer>, ISpecialPlayerInventory {
|
||||
|
||||
private final List<Content> slots;
|
||||
private final int size;
|
||||
private ServerPlayer owner;
|
||||
private int maxStackSize = 99;
|
||||
private CraftInventory bukkitEntity;
|
||||
public List<HumanEntity> transaction = new ArrayList<>();
|
||||
|
||||
public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) {
|
||||
owner = PlayerManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Get total size, rounding up to nearest 9 for client compatibility.
|
||||
int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1;
|
||||
size = ((int) Math.ceil(rawSize / 9.0)) * 9;
|
||||
|
||||
slots = NonNullList.withSize(size, new ContentViewOnly(owner));
|
||||
setupSlots();
|
||||
}
|
||||
|
||||
private void setupSlots() {
|
||||
// Top of inventory: Regular contents.
|
||||
int nextIndex = addMainInventory();
|
||||
|
||||
// If inventory is expected size, we can arrange slots to be pretty.
|
||||
Inventory ownerInv = owner.getInventory();
|
||||
if (ownerInv.items.size() == 36
|
||||
&& ownerInv.armor.size() == 4
|
||||
&& ownerInv.offhand.size() == 1
|
||||
&& owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) {
|
||||
// Armor slots: Bottom left.
|
||||
addArmor(36);
|
||||
// Off-hand: Below chestplate.
|
||||
addOffHand(46);
|
||||
// Drop slot: Bottom right.
|
||||
slots.set(53, new ContentDrop(owner));
|
||||
// Cursor slot: Above drop.
|
||||
slots.set(44, new ContentCursor(owner));
|
||||
|
||||
// Crafting is displayed in the bottom right corner.
|
||||
// As we're using the pretty view, this is a 3x2.
|
||||
addCrafting(41, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we'll just add elements linearly.
|
||||
nextIndex = addArmor(nextIndex);
|
||||
nextIndex = addOffHand(nextIndex);
|
||||
nextIndex = addCrafting(nextIndex, false);
|
||||
slots.set(nextIndex, new ContentCursor(owner));
|
||||
// Drop slot last.
|
||||
slots.set(slots.size() - 1, new ContentDrop(owner));
|
||||
}
|
||||
|
||||
private int addMainInventory() {
|
||||
int listSize = owner.getInventory().items.size();
|
||||
// Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory,
|
||||
// so everything else needs to move up a row.
|
||||
int hotbarDiff = listSize - 9;
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
InventoryType.SlotType type;
|
||||
int invIndex;
|
||||
if (localIndex < hotbarDiff) {
|
||||
invIndex = localIndex + 9;
|
||||
type = InventoryType.SlotType.CONTAINER;
|
||||
} else {
|
||||
type = InventoryType.SlotType.QUICKBAR;
|
||||
invIndex = localIndex - hotbarDiff;
|
||||
}
|
||||
|
||||
slots.set(localIndex, new ContentList(owner, invIndex, type) {
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
items = holder.getInventory().items;
|
||||
}
|
||||
});
|
||||
}
|
||||
return listSize;
|
||||
}
|
||||
|
||||
private int addArmor(int startIndex) {
|
||||
int listSize = owner.getInventory().armor.size();
|
||||
|
||||
for (int i = 0; i < listSize; ++i) {
|
||||
// Armor slots go bottom to top; boots are slot 0, helmet is slot 3.
|
||||
// Since we have to display horizontally due to space restrictions,
|
||||
// making the left side the "top" is more user-friendly.
|
||||
int armorIndex;
|
||||
EquipmentSlot slot;
|
||||
switch (i) {
|
||||
case 3 -> {
|
||||
armorIndex = 0;
|
||||
slot = EquipmentSlot.FEET;
|
||||
}
|
||||
case 2 -> {
|
||||
armorIndex = 1;
|
||||
slot = EquipmentSlot.LEGS;
|
||||
}
|
||||
case 1 -> {
|
||||
armorIndex = 2;
|
||||
slot = EquipmentSlot.CHEST;
|
||||
}
|
||||
case 0 -> {
|
||||
armorIndex = 3;
|
||||
slot = EquipmentSlot.HEAD;
|
||||
}
|
||||
default -> {
|
||||
// In the event that new armor slots are added, they can be placed at the end.
|
||||
armorIndex = i;
|
||||
slot = EquipmentSlot.MAINHAND;
|
||||
}
|
||||
}
|
||||
|
||||
slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot));
|
||||
}
|
||||
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
private int addOffHand(int startIndex) {
|
||||
int listSize = owner.getInventory().offhand.size();
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex));
|
||||
}
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
private int addCrafting(int startIndex, boolean pretty) {
|
||||
int listSize = owner.inventoryMenu.getCraftSlots().getContents().size();
|
||||
pretty &= listSize == 4;
|
||||
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
// Pretty display is a 2x2 rather than linear.
|
||||
// If index is in top row, grid is not 2x2, or pretty is disabled, just use current index.
|
||||
// Otherwise, subtract 2 and add 9 to start in the same position on the next row.
|
||||
int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7);
|
||||
|
||||
slots.set(modIndex, new ContentCrafting(owner, localIndex));
|
||||
}
|
||||
|
||||
if (pretty) {
|
||||
slots.set(startIndex + 2, new ContentViewOnly(owner) {
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return Placeholders.craftingOutput;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
slots.set(startIndex + 11, new ContentCraftingResult(owner));
|
||||
}
|
||||
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
public Slot getMenuSlot(int index, int x, int y) {
|
||||
return slots.get(index).asSlot(this, index, x, y);
|
||||
}
|
||||
|
||||
public InventoryType.SlotType getSlotType(int index) {
|
||||
return slots.get(index).getSlotType();
|
||||
}
|
||||
|
||||
public @NotNull Component getTitle(@Nullable ServerPlayer viewer) {
|
||||
MutableComponent component = Component.empty();
|
||||
// Prefix for use with custom bitmap image fonts.
|
||||
if (owner.equals(viewer)) {
|
||||
component.append(
|
||||
Component.translatableWithFallback("openinv.container.inventory.self", "")
|
||||
.withStyle(style -> style
|
||||
.withFont(ResourceLocation.parse("openinv:font/inventory"))
|
||||
.withColor(ChatFormatting.WHITE)));
|
||||
} else {
|
||||
component.append(
|
||||
Component.translatableWithFallback("openinv.container.inventory.other", "")
|
||||
.withStyle(style -> style
|
||||
.withFont(ResourceLocation.parse("openinv:font/inventory"))
|
||||
.withColor(ChatFormatting.WHITE)));
|
||||
}
|
||||
// Normal title: "Inventory - OwnerName"
|
||||
component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName()))
|
||||
.append(Component.translatable("container.inventory"))
|
||||
.append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName()));
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerPlayer getOwnerHandle() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() {
|
||||
if (bukkitEntity == null) {
|
||||
bukkitEntity = new OpenPlayerInventory(this);
|
||||
}
|
||||
return bukkitEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
ServerPlayer newOwner = PlayerManager.getHandle(player);
|
||||
// Only transfer regular inventory - crafting and cursor slots are transient.
|
||||
newOwner.getInventory().replaceWith(owner.getInventory());
|
||||
owner = newOwner;
|
||||
// Update slots to point to new inventory.
|
||||
slots.forEach(slot -> slot.setHolder(newOwner));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {}
|
||||
|
||||
@Override
|
||||
public boolean isInUse() {
|
||||
return !transaction.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return slots.get(index).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int index, int amount) {
|
||||
return slots.get(index).removePartial(amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int index) {
|
||||
return slots.get(index).remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack itemStack) {
|
||||
slots.get(index).set(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return maxStackSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int maxStackSize) {
|
||||
this.maxStackSize = maxStackSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
NonNullList<ItemStack> contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY);
|
||||
for (int i = 0; i < getContainerSize(); ++i) {
|
||||
contents.set(i, getItem(i));
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity viewer) {
|
||||
transaction.add(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity viewer) {
|
||||
transaction.remove(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Player getOwner() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return owner.getBukkitEntity().getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
owner.getInventory().clearContent();
|
||||
owner.inventoryMenu.getCraftSlots().clearContent();
|
||||
owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots());
|
||||
owner.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
return new OpenInventoryMenu(this, serverPlayer, i, viewOnly);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-183
@@ -1,183 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.CustomModelData;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.block.entity.BannerPattern;
|
||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||
import net.minecraft.world.level.block.entity.BannerPatterns;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftRegistry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class Placeholders {
|
||||
|
||||
private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999);
|
||||
public static final @NotNull EnumMap<GameType, ItemStack> BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class);
|
||||
public static @NotNull ItemStack craftingOutput = defaultCraftingOutput();
|
||||
public static @NotNull ItemStack cursor = defaultCursor();
|
||||
public static @NotNull ItemStack drop = defaultDrop();
|
||||
public static @NotNull ItemStack emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET);
|
||||
public static @NotNull ItemStack emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE);
|
||||
public static @NotNull ItemStack emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS);
|
||||
public static @NotNull ItemStack emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS);
|
||||
public static @NotNull ItemStack emptyOffHand = getEmptyShield();
|
||||
public static @NotNull ItemStack notSlot = defaultNotSlot();
|
||||
public static @NotNull ItemStack blockedOffline = defaultBlockedOffline();
|
||||
|
||||
static {
|
||||
for (GameType type : GameType.values()) {
|
||||
// Barrier: "Not available - Creative" etc.
|
||||
ItemStack typeItem = new ItemStack(Items.BARRIER);
|
||||
typeItem.set(
|
||||
DataComponents.ITEM_NAME,
|
||||
Component.translatable("options.narrator.notavailable").append(" - ").append(type.getShortDisplayName()));
|
||||
BLOCKED_GAME_TYPE.put(type, typeItem);
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(@NotNull ConfigurationSection section) throws Exception {
|
||||
craftingOutput = parse(section, "crafting-output", craftingOutput);
|
||||
cursor = parse(section, "cursor", cursor);
|
||||
drop = parse(section, "drop", drop);
|
||||
emptyHelmet = parse(section, "empty-helmet", emptyHelmet);
|
||||
emptyChestplate = parse(section, "empty-chestplate", emptyChestplate);
|
||||
emptyLeggings = parse(section, "empty-leggings", emptyLeggings);
|
||||
emptyBoots = parse(section, "empty-boots", emptyBoots);
|
||||
emptyOffHand = parse(section, "empty-off-hand", emptyOffHand);
|
||||
notSlot = parse(section, "not-a-slot", notSlot);
|
||||
blockedOffline = parse(section, "blocked.offline", blockedOffline);
|
||||
BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", BLOCKED_GAME_TYPE.get(GameType.CREATIVE)));
|
||||
BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", BLOCKED_GAME_TYPE.get(GameType.SPECTATOR)));
|
||||
}
|
||||
|
||||
private static @NotNull ItemStack parse(
|
||||
@NotNull ConfigurationSection section,
|
||||
@NotNull String path,
|
||||
@NotNull ItemStack defaultStack) throws Exception {
|
||||
String itemText = section.getString(path);
|
||||
|
||||
if (itemText == null) {
|
||||
return defaultStack;
|
||||
}
|
||||
|
||||
CompoundTag compoundTag = TagParser.parseTag(itemText);
|
||||
Optional<ItemStack> parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag);
|
||||
return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack);
|
||||
}
|
||||
|
||||
public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) {
|
||||
if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) {
|
||||
return blockedOffline;
|
||||
}
|
||||
|
||||
return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
private static ItemStack defaultCraftingOutput() {
|
||||
// Crafting table: "Crafting"
|
||||
ItemStack itemStack = new ItemStack(Items.CRAFTING_TABLE);
|
||||
itemStack.set(DataComponents.ITEM_NAME, Component.translatable("container.crafting"));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultCursor() {
|
||||
// Cursor-like banner with no tooltip
|
||||
ItemStack itemStack = new ItemStack(Items.WHITE_BANNER);
|
||||
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
|
||||
Registry<BannerPattern> bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN);
|
||||
BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT).value();
|
||||
BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT).value();
|
||||
BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER).value();
|
||||
itemStack.set(DataComponents.BANNER_PATTERNS,
|
||||
new BannerPatternLayers(List.of(
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY))));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultDrop() {
|
||||
// Dropper: "Drop Selected Item"
|
||||
ItemStack itemStack = new ItemStack(Items.DROPPER);
|
||||
// Note: translatable component, not keybind component! We want the text identifying the keybind, not the key.
|
||||
itemStack.set(DataComponents.ITEM_NAME, Component.translatable("key.drop"));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack getEmptyArmor(ItemLike item) {
|
||||
// Inventory-background-grey-ish leather armor with no tooltip
|
||||
ItemStack itemStack = new ItemStack(item);
|
||||
DyedItemColor color = new DyedItemColor(0xC8C8C8, false);
|
||||
itemStack.set(DataComponents.DYED_COLOR, color);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack getEmptyShield() {
|
||||
ItemStack itemStack = new ItemStack(Items.SHIELD);
|
||||
itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA);
|
||||
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
|
||||
Registry<BannerPattern> bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN);
|
||||
BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL).value();
|
||||
BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT).value();
|
||||
BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT).value();
|
||||
BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT).value();
|
||||
BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT).value();
|
||||
itemStack.set(DataComponents.BANNER_PATTERNS,
|
||||
new BannerPatternLayers(List.of(
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK))));
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultNotSlot() {
|
||||
// White pane with no tooltip
|
||||
ItemStack itemStack = new ItemStack(Items.WHITE_STAINED_GLASS_PANE);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultBlockedOffline() {
|
||||
// Barrier: "Not available - Offline"
|
||||
ItemStack itemStack = new ItemStack(Items.BARRIER);
|
||||
itemStack.set(DataComponents.ITEM_NAME,
|
||||
Component.translatable("options.narrator.notavailable")
|
||||
.append(Component.literal(" - "))
|
||||
.append(Component.translatable("gui.socialInteractions.status_offline")));
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private Placeholders() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
}
|
||||
-172
@@ -1,172 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.bukkit;
|
||||
|
||||
import com.lishid.openinv.internal.ViewOnly;
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* A locked down "empty" inventory that rejects plugin interaction.
|
||||
*/
|
||||
public class OpenDummyInventory extends CraftInventory implements ViewOnly {
|
||||
|
||||
private final InventoryType type;
|
||||
|
||||
public OpenDummyInventory(Container inventory, InventoryType type) {
|
||||
super(inventory);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, @Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> addItem(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||
return arrayToHashMap(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||
return arrayToHashMap(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
private static @NotNull HashMap<Integer, ItemStack> arrayToHashMap(@NotNull ItemStack[] items) {
|
||||
HashMap<Integer, ItemStack> ignored = new HashMap<>();
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
ignored.put(index, items[index]);
|
||||
}
|
||||
return ignored;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
return new ItemStack[getSize()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContents(@NotNull ItemStack[] items) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getStorageContents() {
|
||||
return new ItemStack[getSize()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageContents(@NotNull ItemStack[] items) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull Material material) throws IllegalArgumentException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@Nullable ItemStack item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull Material material, int amount) throws IllegalArgumentException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@Nullable ItemStack item, int amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAtLeast(@Nullable ItemStack item, int amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> all(
|
||||
@NotNull Material material) throws IllegalArgumentException {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> all(@Nullable ItemStack item) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int first(@NotNull Material material) throws IllegalArgumentException {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int first(@NotNull ItemStack item) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int firstEmpty() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull Material material) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(int index) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ListIterator<ItemStack> iterator() {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ListIterator<ItemStack> iterator(int index) {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
|
||||
}
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.bukkit;
|
||||
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenDummyPlayerInventory extends OpenDummyInventory implements PlayerInventory {
|
||||
|
||||
public OpenDummyPlayerInventory(Container inventory) {
|
||||
super(inventory, InventoryType.PLAYER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HumanEntity getHolder() {
|
||||
return (HumanEntity) super.getHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getArmorContents() {
|
||||
return new ItemStack[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getExtraContents() {
|
||||
return new ItemStack[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getHelmet() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getChestplate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getLeggings() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getBoots() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(@NotNull EquipmentSlot slot, @Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(@NotNull EquipmentSlot slot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArmorContents(@Nullable ItemStack[] items) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraContents(@Nullable ItemStack[] items) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHelmet(@Nullable ItemStack helmet) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChestplate(@Nullable ItemStack chestplate) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeggings(@Nullable ItemStack leggings) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoots(@Nullable ItemStack boots) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInMainHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInMainHand(@Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInOffHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInOffHand(@Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInHand(@Nullable ItemStack stack) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeldItemSlot() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeldItemSlot(int slot) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-221
@@ -1,221 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.bukkit;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventory;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenPlayerInventory extends CraftInventory implements PlayerInventory {
|
||||
|
||||
public OpenPlayerInventory(@NotNull OpenInventory inventory) {
|
||||
super(inventory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull OpenInventory getInventory() {
|
||||
return (OpenInventory) super.getInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContents(ItemStack[] items) {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
int size = internal.getContainerSize();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
|
||||
for (int index = 0; index < size; ++index) {
|
||||
if (index < items.length) {
|
||||
internal.setItem(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
} else {
|
||||
internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getStorageContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageContents(ItemStack[] items) throws IllegalArgumentException {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().items;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return InventoryType.PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Player getHolder() {
|
||||
return getInventory().getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getArmorContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().armor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArmorContents(@Nullable ItemStack[] items) {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().armor;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getExtraContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraContents(@Nullable ItemStack[] items) {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().offhand;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getHelmet() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.HEAD.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHelmet(@Nullable ItemStack helmet) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.HEAD.getIndex(), CraftItemStack.asNMSCopy(helmet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getChestplate() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.HEAD.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChestplate(@Nullable ItemStack chestplate) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.CHEST.getIndex(), CraftItemStack.asNMSCopy(chestplate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getLeggings() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.LEGS.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeggings(@Nullable ItemStack leggings) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.LEGS.getIndex(), CraftItemStack.asNMSCopy(leggings));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getBoots() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.FEET.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoots(@Nullable ItemStack boots) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.FEET.getIndex(), CraftItemStack.asNMSCopy(boots));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInMainHand() {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
return CraftItemStack.asCraftMirror(internal.getItem(internal.selected));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInMainHand(@Nullable ItemStack item) {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
internal.setItem(internal.selected, CraftItemStack.asNMSCopy(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInOffHand() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.getFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInOffHand(@Nullable ItemStack item) {
|
||||
getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInHand() {
|
||||
return getItemInMainHand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInHand(@Nullable ItemStack stack) {
|
||||
setItemInMainHand(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeldItemSlot() {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
return internal.items.size() - 9 + internal.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeldItemSlot(int slot) {
|
||||
slot %= 9;
|
||||
getInventory().getOwnerHandle().getInventory().selected = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) {
|
||||
return switch (slot) {
|
||||
case HAND -> getItemInMainHand();
|
||||
case OFF_HAND -> getItemInOffHand();
|
||||
case FEET -> getBoots();
|
||||
case LEGS -> getLeggings();
|
||||
case CHEST -> getChestplate();
|
||||
case HEAD -> getHelmet();
|
||||
default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) {
|
||||
switch (slot) {
|
||||
case HAND -> setItemInMainHand(item);
|
||||
case OFF_HAND -> setItemInOffHand(item);
|
||||
case FEET -> setBoots(item);
|
||||
case LEGS -> setLeggings(item);
|
||||
case CHEST -> setChestplate(item);
|
||||
case HEAD -> setHelmet(item);
|
||||
default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.bukkit;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OpenPlayerInventorySelf extends OpenPlayerInventory {
|
||||
|
||||
private final int offset;
|
||||
|
||||
public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) {
|
||||
super(inventory);
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return super.getItem(offset + index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack item) {
|
||||
super.setItem(offset + index, item);
|
||||
}
|
||||
|
||||
}
|
||||
-479
@@ -1,479 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.menu;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenDummyInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotPlaceholder;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.ClickType;
|
||||
import net.minecraft.world.inventory.ContainerData;
|
||||
import net.minecraft.world.inventory.ContainerListener;
|
||||
import net.minecraft.world.inventory.ContainerSynchronizer;
|
||||
import net.minecraft.world.inventory.DataSlot;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventoryView;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}.
|
||||
*/
|
||||
public abstract class OpenChestMenu<T extends Container & ISpecialInventory & InternalOwned<ServerPlayer>>
|
||||
extends AbstractContainerMenu {
|
||||
|
||||
protected static final int BOTTOM_INVENTORY_SIZE = 36;
|
||||
|
||||
protected final T container;
|
||||
protected final ServerPlayer viewer;
|
||||
protected final boolean viewOnly;
|
||||
protected final boolean ownContainer;
|
||||
protected final int topSize;
|
||||
private CraftInventoryView<OpenChestMenu<T>, Inventory> bukkitEntity;
|
||||
// Syncher fields
|
||||
private @Nullable ContainerSynchronizer synchronizer;
|
||||
private final List<DataSlot> dataSlots = new ArrayList<>();
|
||||
private final IntList remoteDataSlots = new IntArrayList();
|
||||
private final List<ContainerListener> containerListeners = new ArrayList<>();
|
||||
private ItemStack remoteCarried = ItemStack.EMPTY;
|
||||
private boolean suppressRemoteUpdates;
|
||||
|
||||
protected OpenChestMenu(
|
||||
@NotNull MenuType<ChestMenu> type,
|
||||
int containerCounter,
|
||||
@NotNull T container,
|
||||
@NotNull ServerPlayer viewer,
|
||||
boolean viewOnly) {
|
||||
super(type, containerCounter);
|
||||
this.container = container;
|
||||
this.viewer = viewer;
|
||||
this.viewOnly = viewOnly;
|
||||
ownContainer = container.getOwnerHandle().equals(viewer);
|
||||
topSize = getTopSize(viewer);
|
||||
|
||||
preSlotSetup();
|
||||
|
||||
int upperRows = topSize / 9;
|
||||
// View's upper inventory - our container
|
||||
for (int row = 0; row < upperRows; ++row) {
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
// x and y for client purposes, but hey, we're thorough here.
|
||||
// Adapted from net.minecraft.world.inventory.ChestMenu
|
||||
int x = 8 + col * 18;
|
||||
int y = 18 + row * 18;
|
||||
int index = row * 9 + col;
|
||||
|
||||
// Guard against weird inventory sizes.
|
||||
if (index >= container.getContainerSize()) {
|
||||
addSlot(new SlotViewOnly(container, index, x, y));
|
||||
continue;
|
||||
}
|
||||
|
||||
Slot slot = getUpperSlot(index, x, y);
|
||||
|
||||
addSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
// View's lower inventory - viewer inventory
|
||||
int playerInvPad = (upperRows - 4) * 18;
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
int x = 8 + col * 18;
|
||||
int y = playerInvPad + row * 18 + 103;
|
||||
addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y));
|
||||
}
|
||||
}
|
||||
// Hotbar
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
int x = 8 + col * 18;
|
||||
int y = playerInvPad + 161;
|
||||
addSlot(new Slot(viewer.getInventory(), col, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull MenuType<ChestMenu> getChestMenuType(int inventorySize) {
|
||||
inventorySize = ((int) Math.ceil(inventorySize / 9.0)) * 9;
|
||||
return switch (inventorySize) {
|
||||
case 9 -> MenuType.GENERIC_9x1;
|
||||
case 18 -> MenuType.GENERIC_9x2;
|
||||
case 27 -> MenuType.GENERIC_9x3;
|
||||
case 36 -> MenuType.GENERIC_9x4;
|
||||
case 45 -> MenuType.GENERIC_9x5;
|
||||
case 54 -> MenuType.GENERIC_9x6;
|
||||
default -> throw new IllegalArgumentException("Inventory size unsupported: " + inventorySize);
|
||||
};
|
||||
}
|
||||
|
||||
protected void preSlotSetup() {}
|
||||
|
||||
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
|
||||
Slot slot = new Slot(container, index, x, y);
|
||||
if (viewOnly) {
|
||||
return SlotViewOnly.wrap(slot);
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> getBukkitView() {
|
||||
if (bukkitEntity == null) {
|
||||
bukkitEntity = createBukkitEntity();
|
||||
}
|
||||
|
||||
return bukkitEntity;
|
||||
}
|
||||
|
||||
protected @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> createBukkitEntity() {
|
||||
Inventory top;
|
||||
if (viewOnly) {
|
||||
top = new OpenDummyInventory(container, container.getBukkitType());
|
||||
} else {
|
||||
top = container.getBukkitInventory();
|
||||
}
|
||||
return new CraftInventoryView<>(viewer.getBukkitEntity(), top, this) {
|
||||
@Override
|
||||
public @Nullable Inventory getInventory(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return null;
|
||||
}
|
||||
return super.getInventory(rawSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertSlot(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
return super.convertSlot(rawSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType.SlotType getSlotType(int slot) {
|
||||
if (viewOnly) {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
return super.getSlotType(slot);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int getTopSize(ServerPlayer viewer) {
|
||||
MenuType<?> menuType = getType();
|
||||
if (menuType == null) {
|
||||
throw new IllegalStateException("MenuType cannot be null!");
|
||||
} else if (menuType == MenuType.GENERIC_9x1) {
|
||||
return 9;
|
||||
} else if (menuType == MenuType.GENERIC_9x2) {
|
||||
return 18;
|
||||
} else if (menuType == MenuType.GENERIC_9x3) {
|
||||
return 27;
|
||||
} else if (menuType == MenuType.GENERIC_9x4) {
|
||||
return 36;
|
||||
} else if (menuType == MenuType.GENERIC_9x5) {
|
||||
return 45;
|
||||
} else if (menuType == MenuType.GENERIC_9x6) {
|
||||
return 54;
|
||||
}
|
||||
// This is a bit gross, but allows us a safe fallthrough.
|
||||
return menuType.create(-1, viewer.getInventory()).slots.size() - BOTTOM_INVENTORY_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake
|
||||
* slots and respects {@link Slot#hasItem()}.
|
||||
*
|
||||
* @param itemStack the stack to quick-move
|
||||
* @param rangeLow the start of the range of slots that can be moved to, inclusive
|
||||
* @param rangeHigh the end of the range of slots that can be moved to, exclusive
|
||||
* @param topDown whether to start at the top of the range or bottom
|
||||
* @return whether the stack was modified as a result of being quick-moved
|
||||
*/
|
||||
@Override
|
||||
protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) {
|
||||
boolean modified = false;
|
||||
boolean stackable = itemStack.isStackable();
|
||||
Slot firstEmpty = null;
|
||||
|
||||
for (int index = topDown ? rangeHigh - 1 : rangeLow;
|
||||
!itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh);
|
||||
index += topDown ? -1 : 1
|
||||
) {
|
||||
Slot slot = slots.get(index);
|
||||
// If the slot cannot be added to, check the next slot.
|
||||
if (slot.isFake() || !slot.mayPlace(itemStack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (slot.hasItem()) {
|
||||
// If the item isn't stackable, check the next slot.
|
||||
if (!stackable) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise, add as many as we can from our stack to the slot.
|
||||
modified |= addToExistingStack(itemStack, slot);
|
||||
} else {
|
||||
// If this is the first empty slot, keep track of it for later use.
|
||||
if (firstEmpty == null) {
|
||||
firstEmpty = slot;
|
||||
}
|
||||
// If the item isn't stackable, we've located the slot we're adding it to, so we're done.
|
||||
if (!stackable) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the item hasn't been fully added yet, add as many as we can to the first open slot.
|
||||
if (!itemStack.isEmpty() && firstEmpty != null) {
|
||||
firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack))));
|
||||
firstEmpty.setChanged();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
private static boolean addToExistingStack(ItemStack itemStack, Slot slot) {
|
||||
ItemStack existing = slot.getItem();
|
||||
|
||||
// If the items aren't the same, we can't add our item.
|
||||
if (!ItemStack.isSameItemSameComponents(itemStack, existing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int max = slot.getMaxStackSize(existing);
|
||||
int existingCount = existing.getCount();
|
||||
|
||||
// If the stack is already full, we can't add more.
|
||||
if (existingCount >= max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int total = existingCount + itemStack.getCount();
|
||||
|
||||
// If the existing item can accept the entirety of our item, we're done!
|
||||
if (total <= max) {
|
||||
itemStack.setCount(0);
|
||||
existing.setCount(total);
|
||||
slot.setChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, add as many as we can.
|
||||
itemStack.shrink(max - existingCount);
|
||||
existing.setCount(max);
|
||||
slot.setChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clicked(int i, int j, ClickType clickType, Player player) {
|
||||
if (viewOnly) {
|
||||
if (clickType == ClickType.QUICK_CRAFT) {
|
||||
sendAllDataToRemote();
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.clicked(i, j, clickType, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Overrides from here on are purely to modify the sync process to send placeholder items.
|
||||
@Override
|
||||
protected Slot addSlot(Slot slot) {
|
||||
slot.index = this.slots.size();
|
||||
this.slots.add(slot);
|
||||
this.lastSlots.add(ItemStack.EMPTY);
|
||||
this.remoteSlots.add(ItemStack.EMPTY);
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSlot addDataSlot(DataSlot dataSlot) {
|
||||
this.dataSlots.add(dataSlot);
|
||||
this.remoteDataSlots.add(0);
|
||||
return dataSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDataSlots(ContainerData containerData) {
|
||||
for (int i = 0; i < containerData.getCount(); i++) {
|
||||
this.addDataSlot(DataSlot.forContainer(containerData, i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSlotListener(ContainerListener containerListener) {
|
||||
if (!this.containerListeners.contains(containerListener)) {
|
||||
this.containerListeners.add(containerListener);
|
||||
this.broadcastChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSynchronizer(ContainerSynchronizer containerSynchronizer) {
|
||||
this.synchronizer = containerSynchronizer;
|
||||
this.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAllDataToRemote() {
|
||||
for (int index = 0; index < slots.size(); ++index) {
|
||||
Slot slot = slots.get(index);
|
||||
this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy());
|
||||
}
|
||||
|
||||
remoteCarried = getCarried().copy();
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
this.remoteDataSlots.set(index, this.dataSlots.get(index).get());
|
||||
}
|
||||
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastCarriedItem() {
|
||||
this.remoteCarried = this.getCarried().copy();
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendCarriedChange(this, this.remoteCarried);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSlotListener(ContainerListener containerListener) {
|
||||
this.containerListeners.remove(containerListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastChanges() {
|
||||
for (int index = 0; index < this.slots.size(); ++index) {
|
||||
Slot slot = this.slots.get(index);
|
||||
ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem();
|
||||
Supplier<ItemStack> supplier = Suppliers.memoize(itemstack::copy);
|
||||
this.triggerSlotListeners(index, itemstack, supplier);
|
||||
this.synchronizeSlotToRemote(index, itemstack, supplier);
|
||||
}
|
||||
|
||||
this.synchronizeCarriedToRemote();
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
DataSlot dataSlot = this.dataSlots.get(index);
|
||||
int j = dataSlot.get();
|
||||
if (dataSlot.checkAndClearUpdateFlag()) {
|
||||
this.updateDataSlotListeners(index, j);
|
||||
}
|
||||
|
||||
this.synchronizeDataSlotToRemote(index, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastFullState() {
|
||||
for (int index = 0; index < this.slots.size(); ++index) {
|
||||
ItemStack itemstack = this.slots.get(index).getItem();
|
||||
this.triggerSlotListeners(index, itemstack, itemstack::copy);
|
||||
}
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
DataSlot containerproperty = this.dataSlots.get(index);
|
||||
if (containerproperty.checkAndClearUpdateFlag()) {
|
||||
this.updateDataSlotListeners(index, containerproperty.get());
|
||||
}
|
||||
}
|
||||
|
||||
this.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
private void updateDataSlotListeners(int i, int j) {
|
||||
for (ContainerListener containerListener : this.containerListeners) {
|
||||
containerListener.dataChanged(this, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerSlotListeners(int index, ItemStack itemStack, Supplier<ItemStack> supplier) {
|
||||
ItemStack itemStack1 = this.lastSlots.get(index);
|
||||
if (!ItemStack.matches(itemStack1, itemStack)) {
|
||||
ItemStack itemStack2 = supplier.get();
|
||||
this.lastSlots.set(index, itemStack2);
|
||||
|
||||
for (ContainerListener containerListener : this.containerListeners) {
|
||||
containerListener.slotChanged(this, index, itemStack2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier<ItemStack> supplier) {
|
||||
if (!this.suppressRemoteUpdates) {
|
||||
ItemStack itemStack1 = this.remoteSlots.get(i);
|
||||
if (!ItemStack.matches(itemStack1, itemStack)) {
|
||||
ItemStack itemstack2 = supplier.get();
|
||||
this.remoteSlots.set(i, itemstack2);
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendSlotChange(this, i, itemstack2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeDataSlotToRemote(int index, int value) {
|
||||
if (!this.suppressRemoteUpdates) {
|
||||
int existing = this.remoteDataSlots.getInt(index);
|
||||
if (existing != value) {
|
||||
this.remoteDataSlots.set(index, value);
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendDataChange(this, index, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeCarriedToRemote() {
|
||||
if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) {
|
||||
this.remoteCarried = this.getCarried().copy();
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendCarriedChange(this, this.remoteCarried);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoteCarried(ItemStack itemstack) {
|
||||
this.remoteCarried = itemstack.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suppressRemoteUpdates() {
|
||||
this.suppressRemoteUpdates = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeRemoteUpdates() {
|
||||
this.suppressRemoteUpdates = false;
|
||||
}
|
||||
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.menu;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OpenEnderChestMenu extends OpenChestMenu<OpenEnderChest> {
|
||||
|
||||
public OpenEnderChestMenu(
|
||||
@NotNull OpenEnderChest enderChest,
|
||||
@NotNull ServerPlayer viewer,
|
||||
int containerId,
|
||||
boolean viewOnly) {
|
||||
super(getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer, viewOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
if (viewOnly) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
// See ChestMenu
|
||||
Slot slot = this.slots.get(index);
|
||||
|
||||
if (slot.isFake() || !slot.hasItem()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack itemStack = slot.getItem();
|
||||
ItemStack original = itemStack.copy();
|
||||
|
||||
if (index < topSize) {
|
||||
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
if (itemStack.isEmpty()) {
|
||||
slot.setByPlayer(ItemStack.EMPTY);
|
||||
} else {
|
||||
slot.setChanged();
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
}
|
||||
-262
@@ -1,262 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.menu;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenDummyPlayerInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.bukkit.OpenPlayerInventorySelf;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentDrop;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.ContentEquipment;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.slot.SlotViewOnly;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftInventoryView;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.inventory.CraftItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenInventoryMenu extends OpenChestMenu<OpenInventory> {
|
||||
|
||||
private int offset;
|
||||
|
||||
public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) {
|
||||
super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly);
|
||||
}
|
||||
|
||||
private static MenuType<ChestMenu> getMenuType(OpenInventory inventory, ServerPlayer viewer) {
|
||||
int size = inventory.getContainerSize();
|
||||
// Disallow duplicate access to own main inventory contents.
|
||||
if (inventory.getOwnerHandle().equals(viewer)) {
|
||||
size -= viewer.getInventory().items.size();
|
||||
size = ((int) Math.ceil(size / 9.0)) * 9;
|
||||
}
|
||||
|
||||
return getChestMenuType(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preSlotSetup() {
|
||||
offset = ownContainer ? viewer.getInventory().items.size() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
|
||||
index += offset;
|
||||
Slot slot = container.getMenuSlot(index, x, y);
|
||||
|
||||
// If the slot cannot be interacted with there's nothing to configure.
|
||||
if (slot.getClass().equals(SlotViewOnly.class)) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
// Remove drop slot if viewer is not allowed to use it.
|
||||
if (slot instanceof ContentDrop.SlotDrop
|
||||
&& (viewOnly || !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity()))) {
|
||||
return new SlotViewOnly(container, index, x, y);
|
||||
}
|
||||
|
||||
if (slot instanceof ContentEquipment.SlotEquipment equipment) {
|
||||
if (viewOnly) {
|
||||
return SlotViewOnly.wrap(slot);
|
||||
}
|
||||
|
||||
Permissions perm = switch (equipment.getEquipmentSlot()) {
|
||||
case HEAD -> Permissions.INVENTORY_SLOT_HEAD_ANY;
|
||||
case CHEST -> Permissions.INVENTORY_SLOT_CHEST_ANY;
|
||||
case LEGS -> Permissions.INVENTORY_SLOT_LEGS_ANY;
|
||||
case FEET -> Permissions.INVENTORY_SLOT_FEET_ANY;
|
||||
// Off-hand can hold anything, not just equipment.
|
||||
default -> null;
|
||||
};
|
||||
|
||||
// If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot.
|
||||
if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) {
|
||||
equipment.onlyEquipmentFor(container.getOwnerHandle());
|
||||
}
|
||||
|
||||
// Equipment slots are a core part of the inventory, so they will always be shown.
|
||||
return slot;
|
||||
}
|
||||
|
||||
// When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above).
|
||||
if (ownContainer && !(slot instanceof ContentDrop.SlotDrop)) {
|
||||
return new SlotViewOnly(container, index, x, y);
|
||||
}
|
||||
|
||||
if (viewOnly) {
|
||||
return SlotViewOnly.wrap(slot);
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull CraftInventoryView<OpenChestMenu<OpenInventory>, Inventory> createBukkitEntity() {
|
||||
org.bukkit.inventory.Inventory bukkitInventory;
|
||||
if (viewOnly) {
|
||||
bukkitInventory = new OpenDummyPlayerInventory(container);
|
||||
} else if (ownContainer) {
|
||||
bukkitInventory = new OpenPlayerInventorySelf(container, offset);
|
||||
} else {
|
||||
bukkitInventory = container.getBukkitInventory();
|
||||
}
|
||||
|
||||
return new CraftInventoryView<>(viewer.getBukkitEntity(), bukkitInventory, this) {
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack getItem(int index) {
|
||||
if (viewOnly || index < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Slot slot = slots.get(index);
|
||||
return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInTop(int rawSlot) {
|
||||
return rawSlot < topSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Inventory getInventory(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return null;
|
||||
}
|
||||
if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) {
|
||||
return null;
|
||||
}
|
||||
Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE,
|
||||
"Slot %s outside of inventory", rawSlot);
|
||||
if (rawSlot > topSize) {
|
||||
return getBottomInventory();
|
||||
}
|
||||
Slot slot = slots.get(rawSlot);
|
||||
if (slot.isFake()) {
|
||||
return null;
|
||||
}
|
||||
return getTopInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertSlot(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
if (rawSlot < 0) {
|
||||
return rawSlot;
|
||||
}
|
||||
if (rawSlot < topSize) {
|
||||
Slot slot = slots.get(rawSlot);
|
||||
if (slot.isFake()) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
return rawSlot;
|
||||
}
|
||||
|
||||
int slot = rawSlot - topSize;
|
||||
|
||||
if (slot >= 27) {
|
||||
slot -= 27;
|
||||
} else {
|
||||
slot += 9;
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType.SlotType getSlotType(int slot) {
|
||||
if (viewOnly || slot < 0) {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
if (slot >= topSize) {
|
||||
slot -= topSize;
|
||||
if (slot >= 27) {
|
||||
return InventoryType.SlotType.QUICKBAR;
|
||||
}
|
||||
return InventoryType.SlotType.CONTAINER;
|
||||
}
|
||||
return OpenInventoryMenu.this.container.getSlotType(offset + slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countSlots() {
|
||||
return topSize + BOTTOM_INVENTORY_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
if (viewOnly) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
// See ChestMenu and InventoryMenu
|
||||
Slot slot = this.slots.get(index);
|
||||
|
||||
if (!slot.hasItem() || slot.isFake()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack itemStack = slot.getItem();
|
||||
ItemStack originalStack = itemStack.copy();
|
||||
|
||||
if (index < topSize) {
|
||||
// If we're moving top to bottom, do a normal transfer.
|
||||
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else {
|
||||
EquipmentSlot equipmentSlot = player.getEquipmentSlotForItem(itemStack);
|
||||
boolean movedGear = switch (equipmentSlot) {
|
||||
// If this is gear, try to move it to the correct slot first.
|
||||
case OFFHAND, FEET, LEGS, CHEST, HEAD -> {
|
||||
// Locate the correct slot in the contents following the main inventory.
|
||||
for (int extra = container.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) {
|
||||
Slot extraSlot = getSlot(extra);
|
||||
if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot
|
||||
&& equipSlot.getEquipmentSlot() == equipmentSlot) {
|
||||
// If we've found a matching slot, try to move to it.
|
||||
// If this succeeds, even partially, we will not attempt to move to other slots.
|
||||
// Otherwise, armor is already occupied, so we'll fall through to main inventory.
|
||||
yield this.moveItemStackTo(itemStack, extra, extra + 1, false);
|
||||
}
|
||||
}
|
||||
yield false;
|
||||
}
|
||||
// Non-gear gets no special treatment.
|
||||
default -> false;
|
||||
};
|
||||
|
||||
// If main inventory is not available, there's nowhere else to move.
|
||||
if (offset != 0) {
|
||||
if (!movedGear) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else {
|
||||
// If we didn't move to a gear slot, try to move to a main inventory slot.
|
||||
if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().items.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemStack.isEmpty()) {
|
||||
slot.setByPlayer(ItemStack.EMPTY);
|
||||
} else {
|
||||
slot.setChanged();
|
||||
}
|
||||
|
||||
return originalStack;
|
||||
}
|
||||
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* An interface defining behaviors for entries in a {@link Container}. Used to reduce duplicate content reordering.
|
||||
*/
|
||||
public interface Content {
|
||||
|
||||
/**
|
||||
* Update internal holder.
|
||||
*
|
||||
* @param holder the new holder
|
||||
*/
|
||||
void setHolder(@NotNull ServerPlayer holder);
|
||||
|
||||
/**
|
||||
* Get the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack get();
|
||||
|
||||
/**
|
||||
* Remove the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack remove();
|
||||
|
||||
/**
|
||||
* Remove some of the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack removePartial(int amount);
|
||||
|
||||
/**
|
||||
* Set the current item. If slot is currently not usable, will drop item instead.
|
||||
*
|
||||
* @param itemStack the item to set
|
||||
*/
|
||||
void set(ItemStack itemStack);
|
||||
|
||||
/**
|
||||
* Get a {@link Slot} for use in a {@link net.minecraft.world.inventory.AbstractContainerMenu ContainerMenu}. Will
|
||||
* impose any specific restrictions to insertion or removal.
|
||||
*
|
||||
* @param container the backing container
|
||||
* @param slot the slot of the backing container represented
|
||||
* @param x clientside x dimension from top left of inventory, not used
|
||||
* @param y clientside y dimension from top left of inventory, not used
|
||||
* @return a menu slot
|
||||
*/
|
||||
Slot asSlot(Container container, int slot, int x, int y);
|
||||
|
||||
/**
|
||||
* Get a loose Bukkit translation of what this slot stores. For example, any slot that drops items at the owner rather
|
||||
* than insert them will report itself as being {@link org.bukkit.event.inventory.InventoryType.SlotType#OUTSIDE}.
|
||||
*
|
||||
* @return the closes Bukkit slot type
|
||||
*/
|
||||
org.bukkit.event.inventory.InventoryType.SlotType getSlotType();
|
||||
|
||||
}
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A slot in a survival crafting inventory. Unavailable when not online in a survival mode.
|
||||
*/
|
||||
public class ContentCrafting implements Content {
|
||||
|
||||
private final int index;
|
||||
private ServerPlayer holder;
|
||||
private List<ItemStack> items;
|
||||
|
||||
public ContentCrafting(@NotNull ServerPlayer holder, int index) {
|
||||
setHolder(holder);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
private boolean isAvailable() {
|
||||
return isAvailable(holder);
|
||||
}
|
||||
|
||||
static boolean isAvailable(@NotNull ServerPlayer holder) {
|
||||
// Player must be online and not in creative - since the creative client is (semi-)authoritative,
|
||||
// it ignores changes without extra help, and will delete the item as a result.
|
||||
// Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory.
|
||||
return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
// Note: CraftingContainer#getItems is immutable! Be careful with updates.
|
||||
this.items = holder.inventoryMenu.getCraftSlots().getContents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return isAvailable() ? items.get(index) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
if (!this.isAvailable()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
ItemStack removed = items.remove(index);
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
if (!this.isAvailable()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
ItemStack removed = ContainerHelper.removeItem(items, index, amount);
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
if (isAvailable()) {
|
||||
items.set(index, itemStack);
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
} else {
|
||||
holder.drop(itemStack, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotCrafting(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return isAvailable() ? InventoryType.SlotType.CRAFTING : InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotCrafting extends SlotPlaceholder {
|
||||
|
||||
private SlotCrafting(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return isAvailable() ? items.get(ContentCrafting.this.index) : Placeholders.survivalOnly(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player player) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return isAvailable() && super.hasItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return !isAvailable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot allowing viewing of the crafting result.
|
||||
*
|
||||
* <p>Unmodifiable because I said so. Use your own crafting grid.</p>
|
||||
*/
|
||||
public class ContentCraftingResult extends ContentViewOnly {
|
||||
|
||||
public ContentCraftingResult(@NotNull ServerPlayer holder) {
|
||||
super(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
InventoryMenu inventoryMenu = holder.inventoryMenu;
|
||||
return inventoryMenu.getResultSlot().getItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
if (!ContentCrafting.isAvailable(holder)) {
|
||||
return Placeholders.survivalOnly(holder);
|
||||
}
|
||||
InventoryMenu inventoryMenu = holder.inventoryMenu;
|
||||
return inventoryMenu.getResultSlot().getItem();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.RESULT;
|
||||
}
|
||||
|
||||
}
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot wrapping the active menu's cursor. Unavailable when not online in a survival mode.
|
||||
*/
|
||||
public class ContentCursor implements Content {
|
||||
|
||||
private @NotNull ServerPlayer holder;
|
||||
|
||||
public ContentCursor(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return isAvailable() ? holder.containerMenu.getCarried() : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
holder.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
return carried;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
if (!carried.isEmpty() && carried.getCount() >= amount) {
|
||||
ItemStack value = carried.split(amount);
|
||||
if (carried.isEmpty()) {
|
||||
holder.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
if (isAvailable()) {
|
||||
holder.containerMenu.setCarried(itemStack);
|
||||
} else {
|
||||
holder.drop(itemStack, false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAvailable() {
|
||||
// Player must be online and not in creative - since the creative client is (semi-)authoritative,
|
||||
// it ignores changes without extra help, and will delete the item as a result.
|
||||
// Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory.
|
||||
return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotCursor(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
// As close as possible to "not real"
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotCursor extends SlotPlaceholder {
|
||||
|
||||
private SlotCursor(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
if (!isAvailable()) {
|
||||
return Placeholders.survivalOnly(holder);
|
||||
}
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
return carried.isEmpty() ? Placeholders.cursor : carried;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player player) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return isAvailable() && super.hasItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A fake slot used to drop items. Unavailable offline.
|
||||
*/
|
||||
public class ContentDrop implements Content {
|
||||
|
||||
private ServerPlayer holder;
|
||||
|
||||
public ContentDrop(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
holder.drop(itemStack, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotDrop(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
// Behaves like dropping an item outside the screen, just by the target player.
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotDrop extends SlotPlaceholder {
|
||||
|
||||
private SlotDrop(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return holder.connection != null && !holder.connection.isDisconnected()
|
||||
? Placeholders.drop
|
||||
: Placeholders.blockedOffline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return holder.connection != null && !holder.connection.isDisconnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot for equipment that displays placeholders if empty.
|
||||
*/
|
||||
public class ContentEquipment extends ContentList {
|
||||
|
||||
private final ItemStack placeholder;
|
||||
private final EquipmentSlot equipmentSlot;
|
||||
|
||||
public ContentEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) {
|
||||
super(holder, index, InventoryType.SlotType.ARMOR);
|
||||
placeholder = switch (equipmentSlot) {
|
||||
case HEAD -> Placeholders.emptyHelmet;
|
||||
case CHEST -> Placeholders.emptyChestplate;
|
||||
case LEGS -> Placeholders.emptyLeggings;
|
||||
case FEET -> Placeholders.emptyBoots;
|
||||
default -> Placeholders.emptyOffHand;
|
||||
};
|
||||
this.equipmentSlot = equipmentSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.items = holder.getInventory().armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotEquipment(container, slot, x, y);
|
||||
}
|
||||
|
||||
public class SlotEquipment extends SlotPlaceholder {
|
||||
|
||||
private ServerPlayer viewer;
|
||||
|
||||
SlotEquipment(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
ItemStack itemStack = getItem();
|
||||
if (!itemStack.isEmpty()) {
|
||||
return itemStack;
|
||||
}
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
public EquipmentSlot getEquipmentSlot() {
|
||||
return equipmentSlot;
|
||||
}
|
||||
|
||||
public void onlyEquipmentFor(ServerPlayer viewer) {
|
||||
this.viewer = viewer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack var0) {
|
||||
if (viewer == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(var0) == equipmentSlot;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A normal slot backed by an item list.
|
||||
*/
|
||||
public abstract class ContentList implements Content {
|
||||
|
||||
private final int index;
|
||||
private final InventoryType.SlotType slotType;
|
||||
protected List<ItemStack> items;
|
||||
|
||||
public ContentList(ServerPlayer holder, int index, InventoryType.SlotType slotType) {
|
||||
this.index = index;
|
||||
this.slotType = slotType;
|
||||
setHolder(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return items.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
ItemStack removed = items.remove(index);
|
||||
return removed == null || removed.isEmpty() ? ItemStack.EMPTY : removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ContainerHelper.removeItem(items, index, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
items.set(index, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new Slot(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return slotType;
|
||||
}
|
||||
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot for equipment that updates held items if necessary.
|
||||
*/
|
||||
public class ContentOffHand extends ContentEquipment {
|
||||
|
||||
private ServerPlayer holder;
|
||||
|
||||
public ContentOffHand(ServerPlayer holder, int localIndex) {
|
||||
super(holder, localIndex, EquipmentSlot.OFFHAND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.items = holder.getInventory().offhand;
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.QUICKBAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotEquipment(container, slot, x, y) {
|
||||
@Override
|
||||
public void setChanged() {
|
||||
if (holder.connection != null
|
||||
&& !holder.connection.isDisconnected()
|
||||
&& holder.containerMenu != holder.inventoryMenu) {
|
||||
holder.connection.send(
|
||||
new ClientboundContainerSetSlotPacket(
|
||||
holder.inventoryMenu.containerId,
|
||||
holder.inventoryMenu.incrementStateId(),
|
||||
InventoryMenu.SHIELD_SLOT,
|
||||
holder.getOffhandItem()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A view-only slot that can't be interacted with.
|
||||
*/
|
||||
public class ContentViewOnly implements Content {
|
||||
|
||||
@NotNull ServerPlayer holder;
|
||||
|
||||
public ContentViewOnly(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
this.holder.drop(itemStack, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* An implementation of a slot as used by a menu that may have fake placeholder items.
|
||||
*
|
||||
* <p>Used to prevent plugins (particularly sorting plugins) from adding placeholders to inventories.</p>
|
||||
*/
|
||||
public abstract class SlotPlaceholder extends Slot {
|
||||
|
||||
public SlotPlaceholder(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
public abstract ItemStack getOrDefault();
|
||||
|
||||
}
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.Placeholders;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A view-only {@link Slot}. "Blank" by default, but can wrap another slot to display its content.
|
||||
*/
|
||||
public class SlotViewOnly extends SlotPlaceholder {
|
||||
|
||||
public static @NotNull SlotViewOnly wrap(@NotNull Slot wrapped) {
|
||||
SlotViewOnly wrapper;
|
||||
if (wrapped instanceof SlotPlaceholder placeholder) {
|
||||
wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return placeholder.getOrDefault();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return wrapped.getItem();
|
||||
}
|
||||
};
|
||||
}
|
||||
wrapper.index = wrapped.index;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public SlotViewOnly(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return Placeholders.notSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuickCraft(ItemStack var0, ItemStack var1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTake(Player var0, ItemStack var1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByPlayer(ItemStack newStack) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByPlayer(ItemStack newStack, ItemStack oldStack) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack var0) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize(ItemStack itemStack) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ItemStack> tryRemove(int var0, int var1, Player var2) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeTake(int var0, int var1, Player var2) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeInsert(ItemStack itemStack) {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeInsert(ItemStack itemStack, int amount) {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowModification(Player var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSlot() {
|
||||
return this.slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHighlightable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
-178
@@ -1,178 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.player;
|
||||
|
||||
import com.lishid.openinv.event.OpenEvents;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.storage.PlayerDataStorage;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
public class OpenPlayer extends CraftPlayer {
|
||||
|
||||
/**
|
||||
* List of tags to always reset when saving.
|
||||
*
|
||||
* @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag)
|
||||
* @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
|
||||
* @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag)
|
||||
* @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag)
|
||||
*/
|
||||
private static final Set<String> RESET_TAGS = Set.of(
|
||||
// Entity#saveWithoutId(CompoundTag)
|
||||
"CustomName",
|
||||
"CustomNameVisible",
|
||||
"Silent",
|
||||
"NoGravity",
|
||||
"Glowing",
|
||||
"TicksFrozen",
|
||||
"HasVisualFire",
|
||||
"Tags",
|
||||
"Passengers",
|
||||
// ServerPlayer#addAdditionalSaveData(CompoundTag)
|
||||
// Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle
|
||||
"warden_spawn_tracker",
|
||||
"enteredNetherPosition",
|
||||
"SpawnX",
|
||||
"SpawnY",
|
||||
"SpawnZ",
|
||||
"SpawnForced",
|
||||
"SpawnAngle",
|
||||
"SpawnDimension",
|
||||
"raid_omen_position",
|
||||
"ender_pearls",
|
||||
// Player#addAdditionalSaveData(CompoundTag)
|
||||
"ShoulderEntityLeft",
|
||||
"ShoulderEntityRight",
|
||||
"LastDeathLocation",
|
||||
"current_explosion_impact_pos",
|
||||
// LivingEntity#addAdditionalSaveData(CompoundTag)
|
||||
"active_effects",
|
||||
"SleepingX",
|
||||
"SleepingY",
|
||||
"SleepingZ",
|
||||
"Brain"
|
||||
);
|
||||
|
||||
private final PlayerManager manager;
|
||||
|
||||
OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) {
|
||||
super(server, entity);
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadData() {
|
||||
manager.loadData(getHandle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData() {
|
||||
if (OpenEvents.saveCancelled(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = this.getHandle();
|
||||
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
|
||||
try {
|
||||
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
|
||||
|
||||
CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null);
|
||||
CompoundTag playerData = getWritableTag(oldData);
|
||||
playerData = player.saveWithoutId(playerData);
|
||||
setExtraData(playerData);
|
||||
|
||||
if (oldData != null) {
|
||||
// Revert certain special data values when offline.
|
||||
revertSpecialValues(playerData, oldData);
|
||||
}
|
||||
|
||||
Path playerDataDir = worldNBTStorage.getPlayerDir().toPath();
|
||||
Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
|
||||
NbtIo.writeCompressed(playerData, tempFile);
|
||||
Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
|
||||
Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
|
||||
Util.safeReplaceFile(dataFile, tempFile, backupFile);
|
||||
} catch (Exception e) {
|
||||
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("null -> new")
|
||||
private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) {
|
||||
if (oldData == null) {
|
||||
return new CompoundTag();
|
||||
}
|
||||
|
||||
// Copy old data. This is a deep clone, so operating on it should be safe.
|
||||
oldData = oldData.copy();
|
||||
|
||||
// Remove vanilla/server data that is not written every time.
|
||||
oldData.getAllKeys()
|
||||
.removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit"));
|
||||
|
||||
return oldData;
|
||||
}
|
||||
|
||||
private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) {
|
||||
// Revert automatic updates to play timestamps.
|
||||
copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class);
|
||||
copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class);
|
||||
copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class);
|
||||
}
|
||||
|
||||
private <T extends Tag> void copyValue(
|
||||
@NotNull CompoundTag source,
|
||||
@NotNull CompoundTag target,
|
||||
@NotNull String container,
|
||||
@NotNull String key,
|
||||
@SuppressWarnings("SameParameterValue") @NotNull Class<T> tagType) {
|
||||
CompoundTag oldContainer = getTag(source, container, CompoundTag.class);
|
||||
CompoundTag newContainer = getTag(target, container, CompoundTag.class);
|
||||
|
||||
// New container being null means the server implementation doesn't store this data.
|
||||
if (newContainer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If old tag exists, copy it to new location, removing otherwise.
|
||||
setTag(newContainer, key, getTag(oldContainer, key, tagType));
|
||||
}
|
||||
|
||||
private <T extends Tag> @Nullable T getTag(
|
||||
@Nullable CompoundTag container,
|
||||
@NotNull String key,
|
||||
@NotNull Class<T> dataType) {
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
Tag value = container.get(key);
|
||||
if (value == null || !dataType.isAssignableFrom(value.getClass())) {
|
||||
return null;
|
||||
}
|
||||
return dataType.cast(value);
|
||||
}
|
||||
|
||||
private <T extends Tag> void setTag(
|
||||
@NotNull CompoundTag container,
|
||||
@NotNull String key,
|
||||
@Nullable T data) {
|
||||
if (data == null) {
|
||||
container.remove(key);
|
||||
} else {
|
||||
container.put(key, data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-267
@@ -1,267 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R2.player;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenEnderChest;
|
||||
import com.lishid.openinv.internal.v1_21_R2.container.OpenInventory;
|
||||
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.ParticleStatus;
|
||||
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.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_21_R2.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_21_R2.event.CraftEventFactory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
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 PlayerManager implements com.lishid.openinv.internal.PlayerManager {
|
||||
|
||||
private static boolean paper;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("io.papermc.paper.configuration.Configuration");
|
||||
paper = true;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
paper = false;
|
||||
}
|
||||
}
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private @Nullable Field bukkitEntity;
|
||||
|
||||
public PlayerManager(@NotNull Logger logger) {
|
||||
this.logger = logger;
|
||||
try {
|
||||
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
|
||||
} catch (NoSuchFieldException e) {
|
||||
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) {
|
||||
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).
|
||||
ParticleStatus.MINIMAL
|
||||
);
|
||||
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);
|
||||
|
||||
try {
|
||||
injectPlayer(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.log(
|
||||
java.util.logging.Level.WARNING,
|
||||
e,
|
||||
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
boolean loadData(@NotNull ServerPlayer player) {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null);
|
||||
|
||||
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);
|
||||
// Game type settings are also loaded separately.
|
||||
player.loadGameTypes(loadedData);
|
||||
|
||||
if (paper) {
|
||||
// Paper: world is not loaded by ServerPlayer#load(CompoundTag).
|
||||
parseWorld(player, loadedData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private 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(logger::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, this));
|
||||
}
|
||||
|
||||
@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) {
|
||||
logger.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 bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) {
|
||||
ServerPlayer player = getHandle(bukkitPlayer);
|
||||
|
||||
if (player.connection == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider)
|
||||
AbstractContainerMenu menu;
|
||||
Component title;
|
||||
if (inventory instanceof OpenInventory playerInv) {
|
||||
menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly);
|
||||
title = playerInv.getTitle(player);
|
||||
} else if (inventory instanceof OpenEnderChest enderChest) {
|
||||
menu = enderChest.createMenu(player, player.nextContainerCounter(), viewOnly);
|
||||
title = enderChest.getTitle();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Should never happen, player is a ServerPlayer with an active connection.
|
||||
if (menu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set up title. Title can only be set once for a menu, and is set during the open process.
|
||||
// Further title changes are a hack where the client is sent a "new" inventory with the same ID,
|
||||
// resulting in a title change but no other state modifications (like cursor position).
|
||||
menu.setTitle(title);
|
||||
|
||||
menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false);
|
||||
|
||||
// Menu is null if event is cancelled.
|
||||
if (menu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
player.containerMenu = menu;
|
||||
player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle()));
|
||||
player.initMenu(menu);
|
||||
|
||||
return menu.getBukkitView();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,77 +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>5.1.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvadapter1_21_R3</artifactId>
|
||||
<name>OpenInvAdapter1_21_R3</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.system>21</maven.compiler.system>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
<spigot.version>1.21.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>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvcommon</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>specialsource-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
-81
@@ -1,81 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3;
|
||||
|
||||
import com.lishid.openinv.internal.Accessor;
|
||||
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.internal.v1_21_R3.container.AnySilentContainer;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class InternalAccessor implements Accessor {
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private final @NotNull PlayerManager manager;
|
||||
private final @NotNull AnySilentContainer anySilentContainer;
|
||||
|
||||
public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
|
||||
this.logger = logger;
|
||||
manager = new PlayerManager(logger);
|
||||
anySilentContainer = new AnySilentContainer(logger, lang);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PlayerManager getPlayerManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IAnySilentContainer getAnySilentContainer() {
|
||||
return anySilentContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) {
|
||||
return new OpenInventory(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) {
|
||||
return new OpenEnderChest(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ISpecialInventory> @Nullable T get(@NotNull Inventory bukkitInventory, @NotNull Class<T> clazz) {
|
||||
if (!(bukkitInventory instanceof CraftInventory craftInventory)) {
|
||||
return null;
|
||||
}
|
||||
Container container = craftInventory.getInventory();
|
||||
if (clazz.isInstance(container)) {
|
||||
return clazz.cast(container);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload(@NotNull ConfigurationSection config) {
|
||||
ConfigurationSection placeholders = config.getConfigurationSection("placeholders");
|
||||
if (placeholders != null) {
|
||||
try {
|
||||
Placeholders.load(placeholders);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-210
@@ -1,210 +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_21_R3.container;
|
||||
|
||||
import com.lishid.openinv.internal.AnySilentContainerBase;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenChestMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager;
|
||||
import com.lishid.openinv.util.ReflectionHelper;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
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.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.state.BlockState;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
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 extends AnySilentContainerBase {
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private final @NotNull LanguageManager lang;
|
||||
private @Nullable Field serverPlayerGameModeGameType;
|
||||
|
||||
public AnySilentContainer(@NotNull Logger logger, @NotNull LanguageManager lang) {
|
||||
this.logger = logger;
|
||||
this.lang = lang;
|
||||
try {
|
||||
try {
|
||||
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
|
||||
this.serverPlayerGameModeGameType.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
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.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 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 = PlayerManager.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 = OpenChestMenu.getChestMenuType(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) {
|
||||
lang.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) {
|
||||
lang.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 = PlayerManager.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) {
|
||||
logger.log(java.util.logging.Level.WARNING, "Error bypassing GameModeChangeEvent", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-196
@@ -1,196 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenEnderChestMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.player.StackedItemContents;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.StackedContentsCompatible;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OpenEnderChest implements Container, StackedContentsCompatible, InternalOwned<ServerPlayer>,
|
||||
ISpecialEnderChest {
|
||||
|
||||
private CraftInventory inventory;
|
||||
private @NotNull ServerPlayer owner;
|
||||
private NonNullList<ItemStack> items;
|
||||
private int maxStack = 64;
|
||||
private final List<HumanEntity> transaction = new ArrayList<>();
|
||||
|
||||
public OpenEnderChest(@NotNull org.bukkit.entity.Player player) {
|
||||
this.owner = PlayerManager.getHandle(player);
|
||||
this.items = owner.getEnderChestInventory().items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ServerPlayer getOwnerHandle() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() {
|
||||
if (inventory == null) {
|
||||
inventory = new CraftInventory(this) {
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return InventoryType.ENDER_CHEST;
|
||||
}
|
||||
};
|
||||
}
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
owner = PlayerManager.getHandle(player);
|
||||
NonNullList<ItemStack> activeItems = owner.getEnderChestInventory().items;
|
||||
|
||||
// Guard against size changing. Theoretically on Purpur all row variations still have 6 rows internally.
|
||||
int max = Math.min(items.size(), activeItems.size());
|
||||
for (int index = 0; index < max; ++index) {
|
||||
activeItems.set(index, items.get(index));
|
||||
}
|
||||
|
||||
items = activeItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return items.stream().allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return index >= 0 && index < items.size() ? items.get(index) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int index, int amount) {
|
||||
ItemStack itemstack = ContainerHelper.removeItem(items, index, amount);
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
setChanged();
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int index) {
|
||||
return index >= 0 && index < items.size() ? items.set(index, ItemStack.EMPTY) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack itemStack) {
|
||||
if (index >= 0 && index < items.size()) {
|
||||
items.set(index, itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return maxStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
this.owner.getEnderChestInventory().setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity craftHumanEntity) {
|
||||
transaction.add(craftHumanEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity craftHumanEntity) {
|
||||
transaction.remove(craftHumanEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Player getOwner() {
|
||||
return getPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int size) {
|
||||
maxStack = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
items.clear();
|
||||
setChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillStackedContents(StackedItemContents stackedContents) {
|
||||
for (ItemStack itemstack : items) {
|
||||
stackedContents.accountStack(itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
public Component getTitle() {
|
||||
return Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName())
|
||||
.append(Component.translatable("container.enderchest"))
|
||||
.append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName()));
|
||||
}
|
||||
|
||||
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-364
@@ -1,364 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenPlayerInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.menu.OpenInventoryMenu;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.Content;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCrafting;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCraftingResult;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentCursor;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentDrop;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentEquipment;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentList;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentOffHand;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentViewOnly;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly;
|
||||
import com.lishid.openinv.internal.v1_21_R3.player.PlayerManager;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftHumanEntity;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OpenInventory implements Container, InternalOwned<ServerPlayer>, ISpecialPlayerInventory {
|
||||
|
||||
private final List<Content> slots;
|
||||
private final int size;
|
||||
private ServerPlayer owner;
|
||||
private int maxStackSize = 99;
|
||||
private CraftInventory bukkitEntity;
|
||||
public List<HumanEntity> transaction = new ArrayList<>();
|
||||
|
||||
public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) {
|
||||
owner = PlayerManager.getHandle(bukkitPlayer);
|
||||
|
||||
// Get total size, rounding up to nearest 9 for client compatibility.
|
||||
int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1;
|
||||
size = ((int) Math.ceil(rawSize / 9.0)) * 9;
|
||||
|
||||
slots = NonNullList.withSize(size, new ContentViewOnly(owner));
|
||||
setupSlots();
|
||||
}
|
||||
|
||||
private void setupSlots() {
|
||||
// Top of inventory: Regular contents.
|
||||
int nextIndex = addMainInventory();
|
||||
|
||||
// If inventory is expected size, we can arrange slots to be pretty.
|
||||
Inventory ownerInv = owner.getInventory();
|
||||
if (ownerInv.items.size() == 36
|
||||
&& ownerInv.armor.size() == 4
|
||||
&& ownerInv.offhand.size() == 1
|
||||
&& owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) {
|
||||
// Armor slots: Bottom left.
|
||||
addArmor(36);
|
||||
// Off-hand: Below chestplate.
|
||||
addOffHand(46);
|
||||
// Drop slot: Bottom right.
|
||||
slots.set(53, new ContentDrop(owner));
|
||||
// Cursor slot: Above drop.
|
||||
slots.set(44, new ContentCursor(owner));
|
||||
|
||||
// Crafting is displayed in the bottom right corner.
|
||||
// As we're using the pretty view, this is a 3x2.
|
||||
addCrafting(41, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we'll just add elements linearly.
|
||||
nextIndex = addArmor(nextIndex);
|
||||
nextIndex = addOffHand(nextIndex);
|
||||
nextIndex = addCrafting(nextIndex, false);
|
||||
slots.set(nextIndex, new ContentCursor(owner));
|
||||
// Drop slot last.
|
||||
slots.set(slots.size() - 1, new ContentDrop(owner));
|
||||
}
|
||||
|
||||
private int addMainInventory() {
|
||||
int listSize = owner.getInventory().items.size();
|
||||
// Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory,
|
||||
// so everything else needs to move up a row.
|
||||
int hotbarDiff = listSize - 9;
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
InventoryType.SlotType type;
|
||||
int invIndex;
|
||||
if (localIndex < hotbarDiff) {
|
||||
invIndex = localIndex + 9;
|
||||
type = InventoryType.SlotType.CONTAINER;
|
||||
} else {
|
||||
type = InventoryType.SlotType.QUICKBAR;
|
||||
invIndex = localIndex - hotbarDiff;
|
||||
}
|
||||
|
||||
slots.set(localIndex, new ContentList(owner, invIndex, type) {
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
items = holder.getInventory().items;
|
||||
}
|
||||
});
|
||||
}
|
||||
return listSize;
|
||||
}
|
||||
|
||||
private int addArmor(int startIndex) {
|
||||
int listSize = owner.getInventory().armor.size();
|
||||
|
||||
for (int i = 0; i < listSize; ++i) {
|
||||
// Armor slots go bottom to top; boots are slot 0, helmet is slot 3.
|
||||
// Since we have to display horizontally due to space restrictions,
|
||||
// making the left side the "top" is more user-friendly.
|
||||
int armorIndex;
|
||||
EquipmentSlot slot;
|
||||
switch (i) {
|
||||
case 3 -> {
|
||||
armorIndex = 0;
|
||||
slot = EquipmentSlot.FEET;
|
||||
}
|
||||
case 2 -> {
|
||||
armorIndex = 1;
|
||||
slot = EquipmentSlot.LEGS;
|
||||
}
|
||||
case 1 -> {
|
||||
armorIndex = 2;
|
||||
slot = EquipmentSlot.CHEST;
|
||||
}
|
||||
case 0 -> {
|
||||
armorIndex = 3;
|
||||
slot = EquipmentSlot.HEAD;
|
||||
}
|
||||
default -> {
|
||||
// In the event that new armor slots are added, they can be placed at the end.
|
||||
armorIndex = i;
|
||||
slot = EquipmentSlot.MAINHAND;
|
||||
}
|
||||
}
|
||||
|
||||
slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot));
|
||||
}
|
||||
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
private int addOffHand(int startIndex) {
|
||||
int listSize = owner.getInventory().offhand.size();
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex));
|
||||
}
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
private int addCrafting(int startIndex, boolean pretty) {
|
||||
int listSize = owner.inventoryMenu.getCraftSlots().getContents().size();
|
||||
pretty &= listSize == 4;
|
||||
|
||||
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
|
||||
// Pretty display is a 2x2 rather than linear.
|
||||
// If index is in top row, grid is not 2x2, or pretty is disabled, just use current index.
|
||||
// Otherwise, subtract 2 and add 9 to start in the same position on the next row.
|
||||
int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7);
|
||||
|
||||
slots.set(modIndex, new ContentCrafting(owner, localIndex));
|
||||
}
|
||||
|
||||
if (pretty) {
|
||||
slots.set(startIndex + 2, new ContentViewOnly(owner) {
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return Placeholders.craftingOutput;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
slots.set(startIndex + 11, new ContentCraftingResult(owner));
|
||||
}
|
||||
|
||||
return startIndex + listSize;
|
||||
}
|
||||
|
||||
public Slot getMenuSlot(int index, int x, int y) {
|
||||
return slots.get(index).asSlot(this, index, x, y);
|
||||
}
|
||||
|
||||
public InventoryType.SlotType getSlotType(int index) {
|
||||
return slots.get(index).getSlotType();
|
||||
}
|
||||
|
||||
public @NotNull Component getTitle(@Nullable ServerPlayer viewer) {
|
||||
MutableComponent component = Component.empty();
|
||||
// Prefix for use with custom bitmap image fonts.
|
||||
if (owner.equals(viewer)) {
|
||||
component.append(
|
||||
Component.translatableWithFallback("openinv.container.inventory.self", "")
|
||||
.withStyle(style -> style
|
||||
.withFont(ResourceLocation.parse("openinv:font/inventory"))
|
||||
.withColor(ChatFormatting.WHITE)));
|
||||
} else {
|
||||
component.append(
|
||||
Component.translatableWithFallback("openinv.container.inventory.other", "")
|
||||
.withStyle(style -> style
|
||||
.withFont(ResourceLocation.parse("openinv:font/inventory"))
|
||||
.withColor(ChatFormatting.WHITE)));
|
||||
}
|
||||
// Normal title: "Inventory - OwnerName"
|
||||
component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName()))
|
||||
.append(Component.translatable("container.inventory"))
|
||||
.append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName()));
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerPlayer getOwnerHandle() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() {
|
||||
if (bukkitEntity == null) {
|
||||
bukkitEntity = new OpenPlayerInventory(this);
|
||||
}
|
||||
return bukkitEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
|
||||
ServerPlayer newOwner = PlayerManager.getHandle(player);
|
||||
// Only transfer regular inventory - crafting and cursor slots are transient.
|
||||
newOwner.getInventory().replaceWith(owner.getInventory());
|
||||
owner = newOwner;
|
||||
// Update slots to point to new inventory.
|
||||
slots.forEach(slot -> slot.setHolder(newOwner));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlayerOffline() {}
|
||||
|
||||
@Override
|
||||
public boolean isInUse() {
|
||||
return !transaction.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull org.bukkit.entity.Player getPlayer() {
|
||||
return getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return slots.get(index).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int index, int amount) {
|
||||
return slots.get(index).removePartial(amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int index) {
|
||||
return slots.get(index).remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack itemStack) {
|
||||
slots.get(index).set(itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return maxStackSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxStackSize(int maxStackSize) {
|
||||
this.maxStackSize = maxStackSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemStack> getContents() {
|
||||
NonNullList<ItemStack> contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY);
|
||||
for (int i = 0; i < getContainerSize(); ++i) {
|
||||
contents.set(i, getItem(i));
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(CraftHumanEntity viewer) {
|
||||
transaction.add(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(CraftHumanEntity viewer) {
|
||||
transaction.remove(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HumanEntity> getViewers() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.bukkit.entity.Player getOwner() {
|
||||
return owner.getBukkitEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return owner.getBukkitEntity().getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
owner.getInventory().clearContent();
|
||||
owner.inventoryMenu.getCraftSlots().clearContent();
|
||||
owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots());
|
||||
owner.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
return new OpenInventoryMenu(this, serverPlayer, i, viewOnly);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-183
@@ -1,183 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.TagParser;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.Unit;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.component.CustomModelData;
|
||||
import net.minecraft.world.item.component.DyedItemColor;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.block.entity.BannerPattern;
|
||||
import net.minecraft.world.level.block.entity.BannerPatternLayers;
|
||||
import net.minecraft.world.level.block.entity.BannerPatterns;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftRegistry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class Placeholders {
|
||||
|
||||
private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(List.of(), List.of(), List.of("openinv:custom"), List.of());
|
||||
public static final @NotNull EnumMap<GameType, ItemStack> BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class);
|
||||
public static @NotNull ItemStack craftingOutput = defaultCraftingOutput();
|
||||
public static @NotNull ItemStack cursor = defaultCursor();
|
||||
public static @NotNull ItemStack drop = defaultDrop();
|
||||
public static @NotNull ItemStack emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET);
|
||||
public static @NotNull ItemStack emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE);
|
||||
public static @NotNull ItemStack emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS);
|
||||
public static @NotNull ItemStack emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS);
|
||||
public static @NotNull ItemStack emptyOffHand = getEmptyShield();
|
||||
public static @NotNull ItemStack notSlot = defaultNotSlot();
|
||||
public static @NotNull ItemStack blockedOffline = defaultBlockedOffline();
|
||||
|
||||
static {
|
||||
for (GameType type : GameType.values()) {
|
||||
// Barrier: "Not available - Creative" etc.
|
||||
ItemStack typeItem = new ItemStack(Items.BARRIER);
|
||||
typeItem.set(
|
||||
DataComponents.ITEM_NAME,
|
||||
Component.translatable("options.narrator.notavailable").append(" - ").append(type.getShortDisplayName()));
|
||||
BLOCKED_GAME_TYPE.put(type, typeItem);
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(@NotNull ConfigurationSection section) throws Exception {
|
||||
craftingOutput = parse(section, "crafting-output", craftingOutput);
|
||||
cursor = parse(section, "cursor", cursor);
|
||||
drop = parse(section, "drop", drop);
|
||||
emptyHelmet = parse(section, "empty-helmet", emptyHelmet);
|
||||
emptyChestplate = parse(section, "empty-chestplate", emptyChestplate);
|
||||
emptyLeggings = parse(section, "empty-leggings", emptyLeggings);
|
||||
emptyBoots = parse(section, "empty-boots", emptyBoots);
|
||||
emptyOffHand = parse(section, "empty-off-hand", emptyOffHand);
|
||||
notSlot = parse(section, "not-a-slot", notSlot);
|
||||
blockedOffline = parse(section, "blocked.offline", blockedOffline);
|
||||
BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", BLOCKED_GAME_TYPE.get(GameType.CREATIVE)));
|
||||
BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", BLOCKED_GAME_TYPE.get(GameType.SPECTATOR)));
|
||||
}
|
||||
|
||||
private static @NotNull ItemStack parse(
|
||||
@NotNull ConfigurationSection section,
|
||||
@NotNull String path,
|
||||
@NotNull ItemStack defaultStack) throws Exception {
|
||||
String itemText = section.getString(path);
|
||||
|
||||
if (itemText == null) {
|
||||
return defaultStack;
|
||||
}
|
||||
|
||||
CompoundTag compoundTag = TagParser.parseTag(itemText);
|
||||
Optional<ItemStack> parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag);
|
||||
return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack);
|
||||
}
|
||||
|
||||
public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) {
|
||||
if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) {
|
||||
return blockedOffline;
|
||||
}
|
||||
|
||||
return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
private static ItemStack defaultCraftingOutput() {
|
||||
// Crafting table: "Crafting"
|
||||
ItemStack itemStack = new ItemStack(Items.CRAFTING_TABLE);
|
||||
itemStack.set(DataComponents.ITEM_NAME, Component.translatable("container.crafting"));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultCursor() {
|
||||
// Cursor-like banner with no tooltip
|
||||
ItemStack itemStack = new ItemStack(Items.WHITE_BANNER);
|
||||
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
|
||||
Registry<BannerPattern> bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN);
|
||||
BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT).value();
|
||||
BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT).value();
|
||||
BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER).value();
|
||||
itemStack.set(DataComponents.BANNER_PATTERNS,
|
||||
new BannerPatternLayers(List.of(
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY))));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultDrop() {
|
||||
// Dropper: "Drop Selected Item"
|
||||
ItemStack itemStack = new ItemStack(Items.DROPPER);
|
||||
// Note: translatable component, not keybind component! We want the text identifying the keybind, not the key.
|
||||
itemStack.set(DataComponents.ITEM_NAME, Component.translatable("key.drop"));
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack getEmptyArmor(ItemLike item) {
|
||||
// Inventory-background-grey-ish leather armor with no tooltip
|
||||
ItemStack itemStack = new ItemStack(item);
|
||||
DyedItemColor color = new DyedItemColor(0xC8C8C8, false);
|
||||
itemStack.set(DataComponents.DYED_COLOR, color);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack getEmptyShield() {
|
||||
ItemStack itemStack = new ItemStack(Items.SHIELD);
|
||||
itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA);
|
||||
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
|
||||
Registry<BannerPattern> bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN);
|
||||
BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL).value();
|
||||
BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT).value();
|
||||
BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT).value();
|
||||
BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT).value();
|
||||
BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT).value();
|
||||
itemStack.set(DataComponents.BANNER_PATTERNS,
|
||||
new BannerPatternLayers(List.of(
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK),
|
||||
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK))));
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultNotSlot() {
|
||||
// White pane with no tooltip
|
||||
ItemStack itemStack = new ItemStack(Items.WHITE_STAINED_GLASS_PANE);
|
||||
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
|
||||
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private static ItemStack defaultBlockedOffline() {
|
||||
// Barrier: "Not available - Offline"
|
||||
ItemStack itemStack = new ItemStack(Items.BARRIER);
|
||||
itemStack.set(DataComponents.ITEM_NAME,
|
||||
Component.translatable("options.narrator.notavailable")
|
||||
.append(Component.literal(" - "))
|
||||
.append(Component.translatable("gui.socialInteractions.status_offline")));
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
private Placeholders() {
|
||||
throw new IllegalStateException("Cannot create instance of utility class.");
|
||||
}
|
||||
|
||||
}
|
||||
-172
@@ -1,172 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.bukkit;
|
||||
|
||||
import com.lishid.openinv.internal.ViewOnly;
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* A locked down "empty" inventory that rejects plugin interaction.
|
||||
*/
|
||||
public class OpenDummyInventory extends CraftInventory implements ViewOnly {
|
||||
|
||||
private final InventoryType type;
|
||||
|
||||
public OpenDummyInventory(Container inventory, InventoryType type) {
|
||||
super(inventory);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, @Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> addItem(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||
return arrayToHashMap(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... items) throws IllegalArgumentException {
|
||||
return arrayToHashMap(items);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
private static @NotNull HashMap<Integer, ItemStack> arrayToHashMap(@NotNull ItemStack[] items) {
|
||||
HashMap<Integer, ItemStack> ignored = new HashMap<>();
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
ignored.put(index, items[index]);
|
||||
}
|
||||
return ignored;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
return new ItemStack[getSize()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContents(@NotNull ItemStack[] items) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getStorageContents() {
|
||||
return new ItemStack[getSize()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageContents(@NotNull ItemStack[] items) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull Material material) throws IllegalArgumentException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@Nullable ItemStack item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull Material material, int amount) throws IllegalArgumentException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@Nullable ItemStack item, int amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAtLeast(@Nullable ItemStack item, int amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> all(
|
||||
@NotNull Material material) throws IllegalArgumentException {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@SuppressWarnings("NonApiType")
|
||||
@Override
|
||||
public @NotNull HashMap<Integer, ItemStack> all(@Nullable ItemStack item) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int first(@NotNull Material material) throws IllegalArgumentException {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int first(@NotNull ItemStack item) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int firstEmpty() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull Material material) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear(int index) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ListIterator<ItemStack> iterator() {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ListIterator<ItemStack> iterator(int index) {
|
||||
return Collections.emptyListIterator();
|
||||
}
|
||||
|
||||
}
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.bukkit;
|
||||
|
||||
import net.minecraft.world.Container;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenDummyPlayerInventory extends OpenDummyInventory implements PlayerInventory {
|
||||
|
||||
public OpenDummyPlayerInventory(Container inventory) {
|
||||
super(inventory, InventoryType.PLAYER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HumanEntity getHolder() {
|
||||
return (HumanEntity) super.getHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getArmorContents() {
|
||||
return new ItemStack[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getExtraContents() {
|
||||
return new ItemStack[4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getHelmet() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getChestplate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getLeggings() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getBoots() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(@NotNull EquipmentSlot slot, @Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(@NotNull EquipmentSlot slot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArmorContents(@Nullable ItemStack[] items) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraContents(@Nullable ItemStack[] items) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHelmet(@Nullable ItemStack helmet) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChestplate(@Nullable ItemStack chestplate) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeggings(@Nullable ItemStack leggings) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoots(@Nullable ItemStack boots) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInMainHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInMainHand(@Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInOffHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInOffHand(@Nullable ItemStack item) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInHand() {
|
||||
return new ItemStack(Material.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInHand(@Nullable ItemStack stack) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeldItemSlot() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeldItemSlot(int slot) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-221
@@ -1,221 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.bukkit;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventory;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenPlayerInventory extends CraftInventory implements PlayerInventory {
|
||||
|
||||
public OpenPlayerInventory(@NotNull OpenInventory inventory) {
|
||||
super(inventory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull OpenInventory getInventory() {
|
||||
return (OpenInventory) super.getInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContents(ItemStack[] items) {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
int size = internal.getContainerSize();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
|
||||
for (int index = 0; index < size; ++index) {
|
||||
if (index < items.length) {
|
||||
internal.setItem(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
} else {
|
||||
internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack[] getStorageContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStorageContents(ItemStack[] items) throws IllegalArgumentException {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().items;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType getType() {
|
||||
return InventoryType.PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Player getHolder() {
|
||||
return getInventory().getOwner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getArmorContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().armor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArmorContents(@Nullable ItemStack[] items) {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().armor;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack[] getExtraContents() {
|
||||
return asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtraContents(@Nullable ItemStack[] items) {
|
||||
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().offhand;
|
||||
int size = list.size();
|
||||
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
|
||||
for (int index = 0; index < items.length; ++index) {
|
||||
list.set(index, CraftItemStack.asNMSCopy(items[index]));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getHelmet() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.HEAD.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHelmet(@Nullable ItemStack helmet) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.HEAD.getIndex(), CraftItemStack.asNMSCopy(helmet));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getChestplate() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.HEAD.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChestplate(@Nullable ItemStack chestplate) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.CHEST.getIndex(), CraftItemStack.asNMSCopy(chestplate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getLeggings() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.LEGS.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLeggings(@Nullable ItemStack leggings) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.LEGS.getIndex(), CraftItemStack.asNMSCopy(leggings));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getBoots() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
|
||||
.getArmor(EquipmentSlot.FEET.getIndex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoots(@Nullable ItemStack boots) {
|
||||
getInventory().getOwnerHandle().getInventory().armor
|
||||
.set(EquipmentSlot.FEET.getIndex(), CraftItemStack.asNMSCopy(boots));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInMainHand() {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
return CraftItemStack.asCraftMirror(internal.getItem(internal.selected));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInMainHand(@Nullable ItemStack item) {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
internal.setItem(internal.selected, CraftItemStack.asNMSCopy(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInOffHand() {
|
||||
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand.getFirst());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInOffHand(@Nullable ItemStack item) {
|
||||
getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemInHand() {
|
||||
return getItemInMainHand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemInHand(@Nullable ItemStack stack) {
|
||||
setItemInMainHand(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeldItemSlot() {
|
||||
Inventory internal = getInventory().getOwnerHandle().getInventory();
|
||||
return internal.items.size() - 9 + internal.selected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeldItemSlot(int slot) {
|
||||
slot %= 9;
|
||||
getInventory().getOwnerHandle().getInventory().selected = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) {
|
||||
return switch (slot) {
|
||||
case HAND -> getItemInMainHand();
|
||||
case OFF_HAND -> getItemInOffHand();
|
||||
case FEET -> getBoots();
|
||||
case LEGS -> getLeggings();
|
||||
case CHEST -> getChestplate();
|
||||
case HEAD -> getHelmet();
|
||||
default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) {
|
||||
switch (slot) {
|
||||
case HAND -> setItemInMainHand(item);
|
||||
case OFF_HAND -> setItemInOffHand(item);
|
||||
case FEET -> setBoots(item);
|
||||
case LEGS -> setLeggings(item);
|
||||
case CHEST -> setChestplate(item);
|
||||
case HEAD -> setHelmet(item);
|
||||
default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.bukkit;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OpenPlayerInventorySelf extends OpenPlayerInventory {
|
||||
|
||||
private final int offset;
|
||||
|
||||
public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) {
|
||||
super(inventory);
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return super.getItem(offset + index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack item) {
|
||||
super.setItem(offset + index, item);
|
||||
}
|
||||
|
||||
}
|
||||
-479
@@ -1,479 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.menu;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.InternalOwned;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenDummyInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotPlaceholder;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.ClickType;
|
||||
import net.minecraft.world.inventory.ContainerData;
|
||||
import net.minecraft.world.inventory.ContainerListener;
|
||||
import net.minecraft.world.inventory.ContainerSynchronizer;
|
||||
import net.minecraft.world.inventory.DataSlot;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventoryView;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}.
|
||||
*/
|
||||
public abstract class OpenChestMenu<T extends Container & ISpecialInventory & InternalOwned<ServerPlayer>>
|
||||
extends AbstractContainerMenu {
|
||||
|
||||
protected static final int BOTTOM_INVENTORY_SIZE = 36;
|
||||
|
||||
protected final T container;
|
||||
protected final ServerPlayer viewer;
|
||||
protected final boolean viewOnly;
|
||||
protected final boolean ownContainer;
|
||||
protected final int topSize;
|
||||
private CraftInventoryView<OpenChestMenu<T>, Inventory> bukkitEntity;
|
||||
// Syncher fields
|
||||
private @Nullable ContainerSynchronizer synchronizer;
|
||||
private final List<DataSlot> dataSlots = new ArrayList<>();
|
||||
private final IntList remoteDataSlots = new IntArrayList();
|
||||
private final List<ContainerListener> containerListeners = new ArrayList<>();
|
||||
private ItemStack remoteCarried = ItemStack.EMPTY;
|
||||
private boolean suppressRemoteUpdates;
|
||||
|
||||
protected OpenChestMenu(
|
||||
@NotNull MenuType<ChestMenu> type,
|
||||
int containerCounter,
|
||||
@NotNull T container,
|
||||
@NotNull ServerPlayer viewer,
|
||||
boolean viewOnly) {
|
||||
super(type, containerCounter);
|
||||
this.container = container;
|
||||
this.viewer = viewer;
|
||||
this.viewOnly = viewOnly;
|
||||
ownContainer = container.getOwnerHandle().equals(viewer);
|
||||
topSize = getTopSize(viewer);
|
||||
|
||||
preSlotSetup();
|
||||
|
||||
int upperRows = topSize / 9;
|
||||
// View's upper inventory - our container
|
||||
for (int row = 0; row < upperRows; ++row) {
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
// x and y for client purposes, but hey, we're thorough here.
|
||||
// Adapted from net.minecraft.world.inventory.ChestMenu
|
||||
int x = 8 + col * 18;
|
||||
int y = 18 + row * 18;
|
||||
int index = row * 9 + col;
|
||||
|
||||
// Guard against weird inventory sizes.
|
||||
if (index >= container.getContainerSize()) {
|
||||
addSlot(new SlotViewOnly(container, index, x, y));
|
||||
continue;
|
||||
}
|
||||
|
||||
Slot slot = getUpperSlot(index, x, y);
|
||||
|
||||
addSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
// View's lower inventory - viewer inventory
|
||||
int playerInvPad = (upperRows - 4) * 18;
|
||||
for (int row = 0; row < 3; ++row) {
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
int x = 8 + col * 18;
|
||||
int y = playerInvPad + row * 18 + 103;
|
||||
addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y));
|
||||
}
|
||||
}
|
||||
// Hotbar
|
||||
for (int col = 0; col < 9; ++col) {
|
||||
int x = 8 + col * 18;
|
||||
int y = playerInvPad + 161;
|
||||
addSlot(new Slot(viewer.getInventory(), col, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull MenuType<ChestMenu> getChestMenuType(int inventorySize) {
|
||||
inventorySize = ((int) Math.ceil(inventorySize / 9.0)) * 9;
|
||||
return switch (inventorySize) {
|
||||
case 9 -> MenuType.GENERIC_9x1;
|
||||
case 18 -> MenuType.GENERIC_9x2;
|
||||
case 27 -> MenuType.GENERIC_9x3;
|
||||
case 36 -> MenuType.GENERIC_9x4;
|
||||
case 45 -> MenuType.GENERIC_9x5;
|
||||
case 54 -> MenuType.GENERIC_9x6;
|
||||
default -> throw new IllegalArgumentException("Inventory size unsupported: " + inventorySize);
|
||||
};
|
||||
}
|
||||
|
||||
protected void preSlotSetup() {}
|
||||
|
||||
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
|
||||
Slot slot = new Slot(container, index, x, y);
|
||||
if (viewOnly) {
|
||||
return SlotViewOnly.wrap(slot);
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> getBukkitView() {
|
||||
if (bukkitEntity == null) {
|
||||
bukkitEntity = createBukkitEntity();
|
||||
}
|
||||
|
||||
return bukkitEntity;
|
||||
}
|
||||
|
||||
protected @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> createBukkitEntity() {
|
||||
Inventory top;
|
||||
if (viewOnly) {
|
||||
top = new OpenDummyInventory(container, container.getBukkitType());
|
||||
} else {
|
||||
top = container.getBukkitInventory();
|
||||
}
|
||||
return new CraftInventoryView<>(viewer.getBukkitEntity(), top, this) {
|
||||
@Override
|
||||
public @Nullable Inventory getInventory(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return null;
|
||||
}
|
||||
return super.getInventory(rawSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertSlot(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
return super.convertSlot(rawSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType.SlotType getSlotType(int slot) {
|
||||
if (viewOnly) {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
return super.getSlotType(slot);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int getTopSize(ServerPlayer viewer) {
|
||||
MenuType<?> menuType = getType();
|
||||
if (menuType == null) {
|
||||
throw new IllegalStateException("MenuType cannot be null!");
|
||||
} else if (menuType == MenuType.GENERIC_9x1) {
|
||||
return 9;
|
||||
} else if (menuType == MenuType.GENERIC_9x2) {
|
||||
return 18;
|
||||
} else if (menuType == MenuType.GENERIC_9x3) {
|
||||
return 27;
|
||||
} else if (menuType == MenuType.GENERIC_9x4) {
|
||||
return 36;
|
||||
} else if (menuType == MenuType.GENERIC_9x5) {
|
||||
return 45;
|
||||
} else if (menuType == MenuType.GENERIC_9x6) {
|
||||
return 54;
|
||||
}
|
||||
// This is a bit gross, but allows us a safe fallthrough.
|
||||
return menuType.create(-1, viewer.getInventory()).slots.size() - BOTTOM_INVENTORY_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake
|
||||
* slots and respects {@link Slot#hasItem()}.
|
||||
*
|
||||
* @param itemStack the stack to quick-move
|
||||
* @param rangeLow the start of the range of slots that can be moved to, inclusive
|
||||
* @param rangeHigh the end of the range of slots that can be moved to, exclusive
|
||||
* @param topDown whether to start at the top of the range or bottom
|
||||
* @return whether the stack was modified as a result of being quick-moved
|
||||
*/
|
||||
@Override
|
||||
protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) {
|
||||
boolean modified = false;
|
||||
boolean stackable = itemStack.isStackable();
|
||||
Slot firstEmpty = null;
|
||||
|
||||
for (int index = topDown ? rangeHigh - 1 : rangeLow;
|
||||
!itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh);
|
||||
index += topDown ? -1 : 1
|
||||
) {
|
||||
Slot slot = slots.get(index);
|
||||
// If the slot cannot be added to, check the next slot.
|
||||
if (slot.isFake() || !slot.mayPlace(itemStack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (slot.hasItem()) {
|
||||
// If the item isn't stackable, check the next slot.
|
||||
if (!stackable) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise, add as many as we can from our stack to the slot.
|
||||
modified |= addToExistingStack(itemStack, slot);
|
||||
} else {
|
||||
// If this is the first empty slot, keep track of it for later use.
|
||||
if (firstEmpty == null) {
|
||||
firstEmpty = slot;
|
||||
}
|
||||
// If the item isn't stackable, we've located the slot we're adding it to, so we're done.
|
||||
if (!stackable) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the item hasn't been fully added yet, add as many as we can to the first open slot.
|
||||
if (!itemStack.isEmpty() && firstEmpty != null) {
|
||||
firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack))));
|
||||
firstEmpty.setChanged();
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
private static boolean addToExistingStack(ItemStack itemStack, Slot slot) {
|
||||
ItemStack existing = slot.getItem();
|
||||
|
||||
// If the items aren't the same, we can't add our item.
|
||||
if (!ItemStack.isSameItemSameComponents(itemStack, existing)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int max = slot.getMaxStackSize(existing);
|
||||
int existingCount = existing.getCount();
|
||||
|
||||
// If the stack is already full, we can't add more.
|
||||
if (existingCount >= max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int total = existingCount + itemStack.getCount();
|
||||
|
||||
// If the existing item can accept the entirety of our item, we're done!
|
||||
if (total <= max) {
|
||||
itemStack.setCount(0);
|
||||
existing.setCount(total);
|
||||
slot.setChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, add as many as we can.
|
||||
itemStack.shrink(max - existingCount);
|
||||
existing.setCount(max);
|
||||
slot.setChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clicked(int i, int j, ClickType clickType, Player player) {
|
||||
if (viewOnly) {
|
||||
if (clickType == ClickType.QUICK_CRAFT) {
|
||||
sendAllDataToRemote();
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.clicked(i, j, clickType, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Overrides from here on are purely to modify the sync process to send placeholder items.
|
||||
@Override
|
||||
protected Slot addSlot(Slot slot) {
|
||||
slot.index = this.slots.size();
|
||||
this.slots.add(slot);
|
||||
this.lastSlots.add(ItemStack.EMPTY);
|
||||
this.remoteSlots.add(ItemStack.EMPTY);
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataSlot addDataSlot(DataSlot dataSlot) {
|
||||
this.dataSlots.add(dataSlot);
|
||||
this.remoteDataSlots.add(0);
|
||||
return dataSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDataSlots(ContainerData containerData) {
|
||||
for (int i = 0; i < containerData.getCount(); i++) {
|
||||
this.addDataSlot(DataSlot.forContainer(containerData, i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSlotListener(ContainerListener containerListener) {
|
||||
if (!this.containerListeners.contains(containerListener)) {
|
||||
this.containerListeners.add(containerListener);
|
||||
this.broadcastChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSynchronizer(ContainerSynchronizer containerSynchronizer) {
|
||||
this.synchronizer = containerSynchronizer;
|
||||
this.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAllDataToRemote() {
|
||||
for (int index = 0; index < slots.size(); ++index) {
|
||||
Slot slot = slots.get(index);
|
||||
this.remoteSlots.set(index, (slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem()).copy());
|
||||
}
|
||||
|
||||
remoteCarried = getCarried().copy();
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
this.remoteDataSlots.set(index, this.dataSlots.get(index).get());
|
||||
}
|
||||
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendInitialData(this, this.remoteSlots, this.remoteCarried, this.remoteDataSlots.toIntArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastCarriedItem() {
|
||||
this.remoteCarried = this.getCarried().copy();
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendCarriedChange(this, this.remoteCarried);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSlotListener(ContainerListener containerListener) {
|
||||
this.containerListeners.remove(containerListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastChanges() {
|
||||
for (int index = 0; index < this.slots.size(); ++index) {
|
||||
Slot slot = this.slots.get(index);
|
||||
ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem();
|
||||
Supplier<ItemStack> supplier = Suppliers.memoize(itemstack::copy);
|
||||
this.triggerSlotListeners(index, itemstack, supplier);
|
||||
this.synchronizeSlotToRemote(index, itemstack, supplier);
|
||||
}
|
||||
|
||||
this.synchronizeCarriedToRemote();
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
DataSlot dataSlot = this.dataSlots.get(index);
|
||||
int j = dataSlot.get();
|
||||
if (dataSlot.checkAndClearUpdateFlag()) {
|
||||
this.updateDataSlotListeners(index, j);
|
||||
}
|
||||
|
||||
this.synchronizeDataSlotToRemote(index, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastFullState() {
|
||||
for (int index = 0; index < this.slots.size(); ++index) {
|
||||
ItemStack itemstack = this.slots.get(index).getItem();
|
||||
this.triggerSlotListeners(index, itemstack, itemstack::copy);
|
||||
}
|
||||
|
||||
for (int index = 0; index < this.dataSlots.size(); ++index) {
|
||||
DataSlot containerproperty = this.dataSlots.get(index);
|
||||
if (containerproperty.checkAndClearUpdateFlag()) {
|
||||
this.updateDataSlotListeners(index, containerproperty.get());
|
||||
}
|
||||
}
|
||||
|
||||
this.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
private void updateDataSlotListeners(int i, int j) {
|
||||
for (ContainerListener containerListener : this.containerListeners) {
|
||||
containerListener.dataChanged(this, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerSlotListeners(int index, ItemStack itemStack, Supplier<ItemStack> supplier) {
|
||||
ItemStack itemStack1 = this.lastSlots.get(index);
|
||||
if (!ItemStack.matches(itemStack1, itemStack)) {
|
||||
ItemStack itemStack2 = supplier.get();
|
||||
this.lastSlots.set(index, itemStack2);
|
||||
|
||||
for (ContainerListener containerListener : this.containerListeners) {
|
||||
containerListener.slotChanged(this, index, itemStack2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier<ItemStack> supplier) {
|
||||
if (!this.suppressRemoteUpdates) {
|
||||
ItemStack itemStack1 = this.remoteSlots.get(i);
|
||||
if (!ItemStack.matches(itemStack1, itemStack)) {
|
||||
ItemStack itemstack2 = supplier.get();
|
||||
this.remoteSlots.set(i, itemstack2);
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendSlotChange(this, i, itemstack2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeDataSlotToRemote(int index, int value) {
|
||||
if (!this.suppressRemoteUpdates) {
|
||||
int existing = this.remoteDataSlots.getInt(index);
|
||||
if (existing != value) {
|
||||
this.remoteDataSlots.set(index, value);
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendDataChange(this, index, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void synchronizeCarriedToRemote() {
|
||||
if (!this.suppressRemoteUpdates && !ItemStack.matches(this.getCarried(), this.remoteCarried)) {
|
||||
this.remoteCarried = this.getCarried().copy();
|
||||
if (this.synchronizer != null) {
|
||||
this.synchronizer.sendCarriedChange(this, this.remoteCarried);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoteCarried(ItemStack itemstack) {
|
||||
this.remoteCarried = itemstack.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suppressRemoteUpdates() {
|
||||
this.suppressRemoteUpdates = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeRemoteUpdates() {
|
||||
this.suppressRemoteUpdates = false;
|
||||
}
|
||||
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.menu;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OpenEnderChestMenu extends OpenChestMenu<OpenEnderChest> {
|
||||
|
||||
public OpenEnderChestMenu(
|
||||
@NotNull OpenEnderChest enderChest,
|
||||
@NotNull ServerPlayer viewer,
|
||||
int containerId,
|
||||
boolean viewOnly) {
|
||||
super(getChestMenuType(enderChest.getContainerSize()), containerId, enderChest, viewer, viewOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
if (viewOnly) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
// See ChestMenu
|
||||
Slot slot = this.slots.get(index);
|
||||
|
||||
if (slot.isFake() || !slot.hasItem()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack itemStack = slot.getItem();
|
||||
ItemStack original = itemStack.copy();
|
||||
|
||||
if (index < topSize) {
|
||||
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
if (itemStack.isEmpty()) {
|
||||
slot.setByPlayer(ItemStack.EMPTY);
|
||||
} else {
|
||||
slot.setChanged();
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
}
|
||||
-262
@@ -1,262 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.menu;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenDummyPlayerInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.bukkit.OpenPlayerInventorySelf;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentDrop;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.ContentEquipment;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.slot.SlotViewOnly;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftInventoryView;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.inventory.CraftItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class OpenInventoryMenu extends OpenChestMenu<OpenInventory> {
|
||||
|
||||
private int offset;
|
||||
|
||||
public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) {
|
||||
super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly);
|
||||
}
|
||||
|
||||
private static MenuType<ChestMenu> getMenuType(OpenInventory inventory, ServerPlayer viewer) {
|
||||
int size = inventory.getContainerSize();
|
||||
// Disallow duplicate access to own main inventory contents.
|
||||
if (inventory.getOwnerHandle().equals(viewer)) {
|
||||
size -= viewer.getInventory().items.size();
|
||||
size = ((int) Math.ceil(size / 9.0)) * 9;
|
||||
}
|
||||
|
||||
return getChestMenuType(size);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preSlotSetup() {
|
||||
offset = ownContainer ? viewer.getInventory().items.size() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
|
||||
index += offset;
|
||||
Slot slot = container.getMenuSlot(index, x, y);
|
||||
|
||||
// If the slot cannot be interacted with there's nothing to configure.
|
||||
if (slot.getClass().equals(SlotViewOnly.class)) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
// Remove drop slot if viewer is not allowed to use it.
|
||||
if (slot instanceof ContentDrop.SlotDrop
|
||||
&& (viewOnly || !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity()))) {
|
||||
return new SlotViewOnly(container, index, x, y);
|
||||
}
|
||||
|
||||
if (slot instanceof ContentEquipment.SlotEquipment equipment) {
|
||||
if (viewOnly) {
|
||||
return SlotViewOnly.wrap(slot);
|
||||
}
|
||||
|
||||
Permissions perm = switch (equipment.getEquipmentSlot()) {
|
||||
case HEAD -> Permissions.INVENTORY_SLOT_HEAD_ANY;
|
||||
case CHEST -> Permissions.INVENTORY_SLOT_CHEST_ANY;
|
||||
case LEGS -> Permissions.INVENTORY_SLOT_LEGS_ANY;
|
||||
case FEET -> Permissions.INVENTORY_SLOT_FEET_ANY;
|
||||
// Off-hand can hold anything, not just equipment.
|
||||
default -> null;
|
||||
};
|
||||
|
||||
// If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot.
|
||||
if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) {
|
||||
equipment.onlyEquipmentFor(container.getOwnerHandle());
|
||||
}
|
||||
|
||||
// Equipment slots are a core part of the inventory, so they will always be shown.
|
||||
return slot;
|
||||
}
|
||||
|
||||
// When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above).
|
||||
if (ownContainer && !(slot instanceof ContentDrop.SlotDrop)) {
|
||||
return new SlotViewOnly(container, index, x, y);
|
||||
}
|
||||
|
||||
if (viewOnly) {
|
||||
return SlotViewOnly.wrap(slot);
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull CraftInventoryView<OpenChestMenu<OpenInventory>, Inventory> createBukkitEntity() {
|
||||
org.bukkit.inventory.Inventory bukkitInventory;
|
||||
if (viewOnly) {
|
||||
bukkitInventory = new OpenDummyPlayerInventory(container);
|
||||
} else if (ownContainer) {
|
||||
bukkitInventory = new OpenPlayerInventorySelf(container, offset);
|
||||
} else {
|
||||
bukkitInventory = container.getBukkitInventory();
|
||||
}
|
||||
|
||||
return new CraftInventoryView<>(viewer.getBukkitEntity(), bukkitInventory, this) {
|
||||
@Override
|
||||
public org.bukkit.inventory.ItemStack getItem(int index) {
|
||||
if (viewOnly || index < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Slot slot = slots.get(index);
|
||||
return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInTop(int rawSlot) {
|
||||
return rawSlot < topSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Inventory getInventory(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return null;
|
||||
}
|
||||
if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) {
|
||||
return null;
|
||||
}
|
||||
Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE,
|
||||
"Slot %s outside of inventory", rawSlot);
|
||||
if (rawSlot > topSize) {
|
||||
return getBottomInventory();
|
||||
}
|
||||
Slot slot = slots.get(rawSlot);
|
||||
if (slot.isFake()) {
|
||||
return null;
|
||||
}
|
||||
return getTopInventory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int convertSlot(int rawSlot) {
|
||||
if (viewOnly) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
if (rawSlot < 0) {
|
||||
return rawSlot;
|
||||
}
|
||||
if (rawSlot < topSize) {
|
||||
Slot slot = slots.get(rawSlot);
|
||||
if (slot.isFake()) {
|
||||
return InventoryView.OUTSIDE;
|
||||
}
|
||||
return rawSlot;
|
||||
}
|
||||
|
||||
int slot = rawSlot - topSize;
|
||||
|
||||
if (slot >= 27) {
|
||||
slot -= 27;
|
||||
} else {
|
||||
slot += 9;
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull InventoryType.SlotType getSlotType(int slot) {
|
||||
if (viewOnly || slot < 0) {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
if (slot >= topSize) {
|
||||
slot -= topSize;
|
||||
if (slot >= 27) {
|
||||
return InventoryType.SlotType.QUICKBAR;
|
||||
}
|
||||
return InventoryType.SlotType.CONTAINER;
|
||||
}
|
||||
return OpenInventoryMenu.this.container.getSlotType(offset + slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countSlots() {
|
||||
return topSize + BOTTOM_INVENTORY_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack quickMoveStack(Player player, int index) {
|
||||
if (viewOnly) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
// See ChestMenu and InventoryMenu
|
||||
Slot slot = this.slots.get(index);
|
||||
|
||||
if (!slot.hasItem() || slot.isFake()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
ItemStack itemStack = slot.getItem();
|
||||
ItemStack originalStack = itemStack.copy();
|
||||
|
||||
if (index < topSize) {
|
||||
// If we're moving top to bottom, do a normal transfer.
|
||||
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else {
|
||||
EquipmentSlot equipmentSlot = player.getEquipmentSlotForItem(itemStack);
|
||||
boolean movedGear = switch (equipmentSlot) {
|
||||
// If this is gear, try to move it to the correct slot first.
|
||||
case OFFHAND, FEET, LEGS, CHEST, HEAD -> {
|
||||
// Locate the correct slot in the contents following the main inventory.
|
||||
for (int extra = container.getOwnerHandle().getInventory().items.size() - offset; extra < topSize; ++extra) {
|
||||
Slot extraSlot = getSlot(extra);
|
||||
if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot
|
||||
&& equipSlot.getEquipmentSlot() == equipmentSlot) {
|
||||
// If we've found a matching slot, try to move to it.
|
||||
// If this succeeds, even partially, we will not attempt to move to other slots.
|
||||
// Otherwise, armor is already occupied, so we'll fall through to main inventory.
|
||||
yield this.moveItemStackTo(itemStack, extra, extra + 1, false);
|
||||
}
|
||||
}
|
||||
yield false;
|
||||
}
|
||||
// Non-gear gets no special treatment.
|
||||
default -> false;
|
||||
};
|
||||
|
||||
// If main inventory is not available, there's nowhere else to move.
|
||||
if (offset != 0) {
|
||||
if (!movedGear) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
} else {
|
||||
// If we didn't move to a gear slot, try to move to a main inventory slot.
|
||||
if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().items.size(), true)) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (itemStack.isEmpty()) {
|
||||
slot.setByPlayer(ItemStack.EMPTY);
|
||||
} else {
|
||||
slot.setChanged();
|
||||
}
|
||||
|
||||
return originalStack;
|
||||
}
|
||||
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* An interface defining behaviors for entries in a {@link Container}. Used to reduce duplicate content reordering.
|
||||
*/
|
||||
public interface Content {
|
||||
|
||||
/**
|
||||
* Update internal holder.
|
||||
*
|
||||
* @param holder the new holder
|
||||
*/
|
||||
void setHolder(@NotNull ServerPlayer holder);
|
||||
|
||||
/**
|
||||
* Get the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack get();
|
||||
|
||||
/**
|
||||
* Remove the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack remove();
|
||||
|
||||
/**
|
||||
* Remove some of the current item.
|
||||
*
|
||||
* @return the current item
|
||||
*/
|
||||
ItemStack removePartial(int amount);
|
||||
|
||||
/**
|
||||
* Set the current item. If slot is currently not usable, will drop item instead.
|
||||
*
|
||||
* @param itemStack the item to set
|
||||
*/
|
||||
void set(ItemStack itemStack);
|
||||
|
||||
/**
|
||||
* Get a {@link Slot} for use in a {@link net.minecraft.world.inventory.AbstractContainerMenu ContainerMenu}. Will
|
||||
* impose any specific restrictions to insertion or removal.
|
||||
*
|
||||
* @param container the backing container
|
||||
* @param slot the slot of the backing container represented
|
||||
* @param x clientside x dimension from top left of inventory, not used
|
||||
* @param y clientside y dimension from top left of inventory, not used
|
||||
* @return a menu slot
|
||||
*/
|
||||
Slot asSlot(Container container, int slot, int x, int y);
|
||||
|
||||
/**
|
||||
* Get a loose Bukkit translation of what this slot stores. For example, any slot that drops items at the owner rather
|
||||
* than insert them will report itself as being {@link org.bukkit.event.inventory.InventoryType.SlotType#OUTSIDE}.
|
||||
*
|
||||
* @return the closes Bukkit slot type
|
||||
*/
|
||||
org.bukkit.event.inventory.InventoryType.SlotType getSlotType();
|
||||
|
||||
}
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A slot in a survival crafting inventory. Unavailable when not online in a survival mode.
|
||||
*/
|
||||
public class ContentCrafting implements Content {
|
||||
|
||||
private final int index;
|
||||
private ServerPlayer holder;
|
||||
private List<ItemStack> items;
|
||||
|
||||
public ContentCrafting(@NotNull ServerPlayer holder, int index) {
|
||||
setHolder(holder);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
private boolean isAvailable() {
|
||||
return isAvailable(holder);
|
||||
}
|
||||
|
||||
static boolean isAvailable(@NotNull ServerPlayer holder) {
|
||||
// Player must be online and not in creative - since the creative client is (semi-)authoritative,
|
||||
// it ignores changes without extra help, and will delete the item as a result.
|
||||
// Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory.
|
||||
return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
// Note: CraftingContainer#getItems is immutable! Be careful with updates.
|
||||
this.items = holder.inventoryMenu.getCraftSlots().getContents();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return isAvailable() ? items.get(index) : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
if (!this.isAvailable()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
ItemStack removed = items.remove(index);
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
if (!this.isAvailable()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
ItemStack removed = ContainerHelper.removeItem(items, index, amount);
|
||||
if (removed.isEmpty()) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
if (isAvailable()) {
|
||||
items.set(index, itemStack);
|
||||
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
|
||||
} else {
|
||||
holder.drop(itemStack, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotCrafting(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return isAvailable() ? InventoryType.SlotType.CRAFTING : InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotCrafting extends SlotPlaceholder {
|
||||
|
||||
private SlotCrafting(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return isAvailable() ? items.get(ContentCrafting.this.index) : Placeholders.survivalOnly(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player player) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return isAvailable() && super.hasItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return !isAvailable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot allowing viewing of the crafting result.
|
||||
*
|
||||
* <p>Unmodifiable because I said so. Use your own crafting grid.</p>
|
||||
*/
|
||||
public class ContentCraftingResult extends ContentViewOnly {
|
||||
|
||||
public ContentCraftingResult(@NotNull ServerPlayer holder) {
|
||||
super(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
InventoryMenu inventoryMenu = holder.inventoryMenu;
|
||||
return inventoryMenu.getResultSlot().getItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
if (!ContentCrafting.isAvailable(holder)) {
|
||||
return Placeholders.survivalOnly(holder);
|
||||
}
|
||||
InventoryMenu inventoryMenu = holder.inventoryMenu;
|
||||
return inventoryMenu.getResultSlot().getItem();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.RESULT;
|
||||
}
|
||||
|
||||
}
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot wrapping the active menu's cursor. Unavailable when not online in a survival mode.
|
||||
*/
|
||||
public class ContentCursor implements Content {
|
||||
|
||||
private @NotNull ServerPlayer holder;
|
||||
|
||||
public ContentCursor(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return isAvailable() ? holder.containerMenu.getCarried() : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
holder.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
return carried;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
if (!carried.isEmpty() && carried.getCount() >= amount) {
|
||||
ItemStack value = carried.split(amount);
|
||||
if (carried.isEmpty()) {
|
||||
holder.containerMenu.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
if (isAvailable()) {
|
||||
holder.containerMenu.setCarried(itemStack);
|
||||
} else {
|
||||
holder.drop(itemStack, false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAvailable() {
|
||||
// Player must be online and not in creative - since the creative client is (semi-)authoritative,
|
||||
// it ignores changes without extra help, and will delete the item as a result.
|
||||
// Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory.
|
||||
return holder.connection != null && !holder.connection.isDisconnected() && holder.gameMode.isSurvival();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotCursor(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
// As close as possible to "not real"
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotCursor extends SlotPlaceholder {
|
||||
|
||||
private SlotCursor(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
if (!isAvailable()) {
|
||||
return Placeholders.survivalOnly(holder);
|
||||
}
|
||||
ItemStack carried = holder.containerMenu.getCarried();
|
||||
return carried.isEmpty() ? Placeholders.cursor : carried;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player player) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return isAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return isAvailable() && super.hasItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A fake slot used to drop items. Unavailable offline.
|
||||
*/
|
||||
public class ContentDrop implements Content {
|
||||
|
||||
private ServerPlayer holder;
|
||||
|
||||
public ContentDrop(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
holder.drop(itemStack, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotDrop(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
// Behaves like dropping an item outside the screen, just by the target player.
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
public class SlotDrop extends SlotPlaceholder {
|
||||
|
||||
private SlotDrop(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return holder.connection != null && !holder.connection.isDisconnected()
|
||||
? Placeholders.drop
|
||||
: Placeholders.blockedOffline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack itemStack) {
|
||||
return holder.connection != null && !holder.connection.isDisconnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot for equipment that displays placeholders if empty.
|
||||
*/
|
||||
public class ContentEquipment extends ContentList {
|
||||
|
||||
private final ItemStack placeholder;
|
||||
private final EquipmentSlot equipmentSlot;
|
||||
|
||||
public ContentEquipment(ServerPlayer holder, int index, EquipmentSlot equipmentSlot) {
|
||||
super(holder, index, InventoryType.SlotType.ARMOR);
|
||||
placeholder = switch (equipmentSlot) {
|
||||
case HEAD -> Placeholders.emptyHelmet;
|
||||
case CHEST -> Placeholders.emptyChestplate;
|
||||
case LEGS -> Placeholders.emptyLeggings;
|
||||
case FEET -> Placeholders.emptyBoots;
|
||||
default -> Placeholders.emptyOffHand;
|
||||
};
|
||||
this.equipmentSlot = equipmentSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.items = holder.getInventory().armor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotEquipment(container, slot, x, y);
|
||||
}
|
||||
|
||||
public class SlotEquipment extends SlotPlaceholder {
|
||||
|
||||
private ServerPlayer viewer;
|
||||
|
||||
SlotEquipment(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
ItemStack itemStack = getItem();
|
||||
if (!itemStack.isEmpty()) {
|
||||
return itemStack;
|
||||
}
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
public EquipmentSlot getEquipmentSlot() {
|
||||
return equipmentSlot;
|
||||
}
|
||||
|
||||
public void onlyEquipmentFor(ServerPlayer viewer) {
|
||||
this.viewer = viewer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack var0) {
|
||||
if (viewer == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(var0) == equipmentSlot;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A normal slot backed by an item list.
|
||||
*/
|
||||
public abstract class ContentList implements Content {
|
||||
|
||||
private final int index;
|
||||
private final InventoryType.SlotType slotType;
|
||||
protected List<ItemStack> items;
|
||||
|
||||
public ContentList(ServerPlayer holder, int index, InventoryType.SlotType slotType) {
|
||||
this.index = index;
|
||||
this.slotType = slotType;
|
||||
setHolder(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return items.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
ItemStack removed = items.remove(index);
|
||||
return removed == null || removed.isEmpty() ? ItemStack.EMPTY : removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ContainerHelper.removeItem(items, index, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
items.set(index, itemStack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new Slot(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return slotType;
|
||||
}
|
||||
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.inventory.InventoryMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A slot for equipment that updates held items if necessary.
|
||||
*/
|
||||
public class ContentOffHand extends ContentEquipment {
|
||||
|
||||
private ServerPlayer holder;
|
||||
|
||||
public ContentOffHand(ServerPlayer holder, int localIndex) {
|
||||
super(holder, localIndex, EquipmentSlot.OFFHAND);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.items = holder.getInventory().offhand;
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.QUICKBAR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotEquipment(container, slot, x, y) {
|
||||
@Override
|
||||
public void setChanged() {
|
||||
if (holder.connection != null
|
||||
&& !holder.connection.isDisconnected()
|
||||
&& holder.containerMenu != holder.inventoryMenu) {
|
||||
holder.connection.send(
|
||||
new ClientboundContainerSetSlotPacket(
|
||||
holder.inventoryMenu.containerId,
|
||||
holder.inventoryMenu.incrementStateId(),
|
||||
InventoryMenu.SHIELD_SLOT,
|
||||
holder.getOffhandItem()));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A view-only slot that can't be interacted with.
|
||||
*/
|
||||
public class ContentViewOnly implements Content {
|
||||
|
||||
@NotNull ServerPlayer holder;
|
||||
|
||||
public ContentViewOnly(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHolder(@NotNull ServerPlayer holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack get() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removePartial(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack itemStack) {
|
||||
this.holder.drop(itemStack, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slot asSlot(Container container, int slot, int x, int y) {
|
||||
return new SlotViewOnly(container, slot, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryType.SlotType getSlotType() {
|
||||
return InventoryType.SlotType.OUTSIDE;
|
||||
}
|
||||
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* An implementation of a slot as used by a menu that may have fake placeholder items.
|
||||
*
|
||||
* <p>Used to prevent plugins (particularly sorting plugins) from adding placeholders to inventories.</p>
|
||||
*/
|
||||
public abstract class SlotPlaceholder extends Slot {
|
||||
|
||||
public SlotPlaceholder(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
public abstract ItemStack getOrDefault();
|
||||
|
||||
}
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.container.slot;
|
||||
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.Placeholders;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A view-only {@link Slot}. "Blank" by default, but can wrap another slot to display its content.
|
||||
*/
|
||||
public class SlotViewOnly extends SlotPlaceholder {
|
||||
|
||||
public static @NotNull SlotViewOnly wrap(@NotNull Slot wrapped) {
|
||||
SlotViewOnly wrapper;
|
||||
if (wrapped instanceof SlotPlaceholder placeholder) {
|
||||
wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return placeholder.getOrDefault();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
wrapper = new SlotViewOnly(wrapped.container, wrapped.slot, wrapped.x, wrapped.y) {
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return wrapped.getItem();
|
||||
}
|
||||
};
|
||||
}
|
||||
wrapper.index = wrapped.index;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public SlotViewOnly(Container container, int index, int x, int y) {
|
||||
super(container, index, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getOrDefault() {
|
||||
return Placeholders.notSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuickCraft(ItemStack var0, ItemStack var1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTake(Player var0, ItemStack var1) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPlace(ItemStack var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem() {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByPlayer(ItemStack newStack) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setByPlayer(ItemStack newStack, ItemStack oldStack) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(ItemStack var0) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackSize(ItemStack itemStack) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack remove(int amount) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mayPickup(Player var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ItemStack> tryRemove(int var0, int var1, Player var2) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeTake(int var0, int var1, Player var2) {
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeInsert(ItemStack itemStack) {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack safeInsert(ItemStack itemStack, int amount) {
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowModification(Player var0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSlot() {
|
||||
return this.slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHighlightable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFake() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
-178
@@ -1,178 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.player;
|
||||
|
||||
import com.lishid.openinv.event.OpenEvents;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.nbt.NumericTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.storage.PlayerDataStorage;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
|
||||
public class OpenPlayer extends CraftPlayer {
|
||||
|
||||
/**
|
||||
* List of tags to always reset when saving.
|
||||
*
|
||||
* @see net.minecraft.world.entity.Entity#saveWithoutId(CompoundTag)
|
||||
* @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
|
||||
* @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag)
|
||||
* @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag)
|
||||
*/
|
||||
private static final Set<String> RESET_TAGS = Set.of(
|
||||
// Entity#saveWithoutId(CompoundTag)
|
||||
"CustomName",
|
||||
"CustomNameVisible",
|
||||
"Silent",
|
||||
"NoGravity",
|
||||
"Glowing",
|
||||
"TicksFrozen",
|
||||
"HasVisualFire",
|
||||
"Tags",
|
||||
"Passengers",
|
||||
// ServerPlayer#addAdditionalSaveData(CompoundTag)
|
||||
// Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle
|
||||
"warden_spawn_tracker",
|
||||
"enteredNetherPosition",
|
||||
"SpawnX",
|
||||
"SpawnY",
|
||||
"SpawnZ",
|
||||
"SpawnForced",
|
||||
"SpawnAngle",
|
||||
"SpawnDimension",
|
||||
"raid_omen_position",
|
||||
"ender_pearls",
|
||||
// Player#addAdditionalSaveData(CompoundTag)
|
||||
"ShoulderEntityLeft",
|
||||
"ShoulderEntityRight",
|
||||
"LastDeathLocation",
|
||||
"current_explosion_impact_pos",
|
||||
// LivingEntity#addAdditionalSaveData(CompoundTag)
|
||||
"active_effects",
|
||||
"SleepingX",
|
||||
"SleepingY",
|
||||
"SleepingZ",
|
||||
"Brain"
|
||||
);
|
||||
|
||||
private final PlayerManager manager;
|
||||
|
||||
OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) {
|
||||
super(server, entity);
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadData() {
|
||||
manager.loadData(getHandle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData() {
|
||||
if (OpenEvents.saveCancelled(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerPlayer player = this.getHandle();
|
||||
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
|
||||
try {
|
||||
PlayerDataStorage worldNBTStorage = player.server.getPlayerList().playerIo;
|
||||
|
||||
CompoundTag oldData = isOnline() ? null : worldNBTStorage.load(player.getName().getString(), player.getStringUUID()).orElse(null);
|
||||
CompoundTag playerData = getWritableTag(oldData);
|
||||
playerData = player.saveWithoutId(playerData);
|
||||
setExtraData(playerData);
|
||||
|
||||
if (oldData != null) {
|
||||
// Revert certain special data values when offline.
|
||||
revertSpecialValues(playerData, oldData);
|
||||
}
|
||||
|
||||
Path playerDataDir = worldNBTStorage.getPlayerDir().toPath();
|
||||
Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
|
||||
NbtIo.writeCompressed(playerData, tempFile);
|
||||
Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
|
||||
Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
|
||||
Util.safeReplaceFile(dataFile, tempFile, backupFile);
|
||||
} catch (Exception e) {
|
||||
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("null -> new")
|
||||
private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) {
|
||||
if (oldData == null) {
|
||||
return new CompoundTag();
|
||||
}
|
||||
|
||||
// Copy old data. This is a deep clone, so operating on it should be safe.
|
||||
oldData = oldData.copy();
|
||||
|
||||
// Remove vanilla/server data that is not written every time.
|
||||
oldData.getAllKeys()
|
||||
.removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit"));
|
||||
|
||||
return oldData;
|
||||
}
|
||||
|
||||
private void revertSpecialValues(@NotNull CompoundTag newData, @NotNull CompoundTag oldData) {
|
||||
// Revert automatic updates to play timestamps.
|
||||
copyValue(oldData, newData, "bukkit", "lastPlayed", NumericTag.class);
|
||||
copyValue(oldData, newData, "Paper", "LastSeen", NumericTag.class);
|
||||
copyValue(oldData, newData, "Paper", "LastLogin", NumericTag.class);
|
||||
}
|
||||
|
||||
private <T extends Tag> void copyValue(
|
||||
@NotNull CompoundTag source,
|
||||
@NotNull CompoundTag target,
|
||||
@NotNull String container,
|
||||
@NotNull String key,
|
||||
@SuppressWarnings("SameParameterValue") @NotNull Class<T> tagType) {
|
||||
CompoundTag oldContainer = getTag(source, container, CompoundTag.class);
|
||||
CompoundTag newContainer = getTag(target, container, CompoundTag.class);
|
||||
|
||||
// New container being null means the server implementation doesn't store this data.
|
||||
if (newContainer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If old tag exists, copy it to new location, removing otherwise.
|
||||
setTag(newContainer, key, getTag(oldContainer, key, tagType));
|
||||
}
|
||||
|
||||
private <T extends Tag> @Nullable T getTag(
|
||||
@Nullable CompoundTag container,
|
||||
@NotNull String key,
|
||||
@NotNull Class<T> dataType) {
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
Tag value = container.get(key);
|
||||
if (value == null || !dataType.isAssignableFrom(value.getClass())) {
|
||||
return null;
|
||||
}
|
||||
return dataType.cast(value);
|
||||
}
|
||||
|
||||
private <T extends Tag> void setTag(
|
||||
@NotNull CompoundTag container,
|
||||
@NotNull String key,
|
||||
@Nullable T data) {
|
||||
if (data == null) {
|
||||
container.remove(key);
|
||||
} else {
|
||||
container.put(key, data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-267
@@ -1,267 +0,0 @@
|
||||
package com.lishid.openinv.internal.v1_21_R3.player;
|
||||
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenEnderChest;
|
||||
import com.lishid.openinv.internal.v1_21_R3.container.OpenInventory;
|
||||
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.ParticleStatus;
|
||||
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.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_21_R3.CraftServer;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.CraftWorld;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer;
|
||||
import org.bukkit.craftbukkit.v1_21_R3.event.CraftEventFactory;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
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 PlayerManager implements com.lishid.openinv.internal.PlayerManager {
|
||||
|
||||
private static boolean paper;
|
||||
|
||||
static {
|
||||
try {
|
||||
Class.forName("io.papermc.paper.configuration.Configuration");
|
||||
paper = true;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
paper = false;
|
||||
}
|
||||
}
|
||||
|
||||
private final @NotNull Logger logger;
|
||||
private @Nullable Field bukkitEntity;
|
||||
|
||||
public PlayerManager(@NotNull Logger logger) {
|
||||
this.logger = logger;
|
||||
try {
|
||||
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
|
||||
} catch (NoSuchFieldException e) {
|
||||
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) {
|
||||
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).
|
||||
ParticleStatus.MINIMAL
|
||||
);
|
||||
|
||||
ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);
|
||||
|
||||
try {
|
||||
injectPlayer(entity);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.log(
|
||||
java.util.logging.Level.WARNING,
|
||||
e,
|
||||
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
boolean loadData(@NotNull ServerPlayer player) {
|
||||
// See CraftPlayer#loadData
|
||||
CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player).orElse(null);
|
||||
|
||||
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);
|
||||
// Game type settings are also loaded separately.
|
||||
player.loadGameTypes(loadedData);
|
||||
|
||||
if (paper) {
|
||||
// Paper: world is not loaded by ServerPlayer#load(CompoundTag).
|
||||
parseWorld(player, loadedData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private 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(logger::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, this));
|
||||
}
|
||||
|
||||
@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) {
|
||||
logger.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 bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) {
|
||||
ServerPlayer player = getHandle(bukkitPlayer);
|
||||
|
||||
if (player.connection == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider)
|
||||
AbstractContainerMenu menu;
|
||||
Component title;
|
||||
if (inventory instanceof OpenInventory playerInv) {
|
||||
menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly);
|
||||
title = playerInv.getTitle(player);
|
||||
} else if (inventory instanceof OpenEnderChest enderChest) {
|
||||
menu = enderChest.createMenu(player, player.nextContainerCounter(), viewOnly);
|
||||
title = enderChest.getTitle();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Should never happen, player is a ServerPlayer with an active connection.
|
||||
if (menu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set up title. Title can only be set once for a menu, and is set during the open process.
|
||||
// Further title changes are a hack where the client is sent a "new" inventory with the same ID,
|
||||
// resulting in a title change but no other state modifications (like cursor position).
|
||||
menu.setTitle(title);
|
||||
|
||||
menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false);
|
||||
|
||||
// Menu is null if event is cancelled.
|
||||
if (menu == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
player.containerMenu = menu;
|
||||
player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle()));
|
||||
player.initMenu(menu);
|
||||
|
||||
return menu.getBukkitView();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
before_install:
|
||||
- sdk install java 21.0.4-tem
|
||||
- sdk use java 21.0.4-tem
|
||||
- mvn wrapper:wrapper -Dmaven=3.9.8
|
||||
-140
@@ -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/>.
|
||||
-->
|
||||
|
||||
<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>5.1.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>openinvplugin</artifactId>
|
||||
<name>OpenInvPlugin</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>planarwrappers</artifactId>
|
||||
<groupId>com.github.jikoo</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvapi</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>openinvcommon</artifactId>
|
||||
<groupId>com.lishid</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_21_R3</artifactId>
|
||||
<version>5.1.6</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lishid</groupId>
|
||||
<artifactId>openinvadapter1_21_R2</artifactId>
|
||||
<version>5.1.6</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>annotations</artifactId>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>OpenInv</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
<!-- Always shade entirety of required modules. -->
|
||||
<artifact>com.lishid:openinv*</artifact>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<!-- Don't warn about file conflicts that'll be clobbered anyway. -->
|
||||
<excludes>
|
||||
<exclude>META-INF/MANIFEST.MF</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>com.github.jikoo.planarwrappers</pattern>
|
||||
<shadedPattern>com.github.jikoo.openinv.planarwrappers</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-final-jar</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.parent.build.directory}</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<includes>
|
||||
<include>${project.build.finalName}.jar</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -1,309 +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.command.ContainerSettingCommand;
|
||||
import com.lishid.openinv.command.OpenInvCommand;
|
||||
import com.lishid.openinv.command.SearchContainerCommand;
|
||||
import com.lishid.openinv.command.SearchEnchantCommand;
|
||||
import com.lishid.openinv.command.SearchInvCommand;
|
||||
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.listener.ContainerListener;
|
||||
import com.lishid.openinv.listener.ToggleListener;
|
||||
import com.lishid.openinv.util.AccessEqualMode;
|
||||
import com.lishid.openinv.util.InternalAccessor;
|
||||
import com.lishid.openinv.util.InventoryManager;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import com.lishid.openinv.util.PlayerLoader;
|
||||
import com.lishid.openinv.util.config.Config;
|
||||
import com.lishid.openinv.util.config.ConfigUpdater;
|
||||
import com.lishid.openinv.util.lang.LangMigrator;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import com.lishid.openinv.util.setting.PlayerToggle;
|
||||
import com.lishid.openinv.util.setting.PlayerToggles;
|
||||
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.InventoryView;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* The main class for OpenInv.
|
||||
*/
|
||||
public class OpenInv extends JavaPlugin implements IOpenInv {
|
||||
|
||||
private InternalAccessor accessor;
|
||||
private Config config;
|
||||
private InventoryManager inventoryManager;
|
||||
private LanguageManager languageManager;
|
||||
private PlayerLoader playerLoader;
|
||||
private boolean isSpigot = false;
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
super.reloadConfig();
|
||||
config.reload(getConfig());
|
||||
languageManager.reload();
|
||||
if (accessor != null && accessor.isSupported()) {
|
||||
accessor.reload(getConfig());
|
||||
}
|
||||
}
|
||||
|
||||
@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() {
|
||||
inventoryManager.evictAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// Save default configuration if not present.
|
||||
this.saveDefaultConfig();
|
||||
|
||||
// Migrate locale files to a subfolder.
|
||||
Path dataFolder = getDataFolder().toPath();
|
||||
new LangMigrator(dataFolder, dataFolder.resolve("locale"), getLogger()).migrate();
|
||||
|
||||
// Set up configurable features. Note that #reloadConfig is called on the first call to #getConfig!
|
||||
// Configuration values should not be accessed until after all of these have been set up.
|
||||
config = new Config();
|
||||
languageManager = new LanguageManager(this, "en");
|
||||
accessor = new InternalAccessor(getLogger(), languageManager);
|
||||
|
||||
// Perform initial config load.
|
||||
reloadConfig();
|
||||
|
||||
inventoryManager = new InventoryManager(this, config, accessor);
|
||||
playerLoader = new PlayerLoader(this, config, inventoryManager, accessor, getLogger());
|
||||
|
||||
try {
|
||||
Class.forName("org.bukkit.entity.Player$Spigot");
|
||||
isSpigot = true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
isSpigot = false;
|
||||
}
|
||||
|
||||
// Version check
|
||||
if (isSpigot && this.accessor.isSupported()) {
|
||||
reloadConfig();
|
||||
|
||||
// Update existing configuration. May require internal access.
|
||||
new ConfigUpdater(this).checkForUpdates();
|
||||
|
||||
// Register relevant event listeners.
|
||||
registerEvents();
|
||||
|
||||
// Register commands to their executors.
|
||||
registerCommands();
|
||||
|
||||
} else {
|
||||
this.sendVersionError(this.getLogger()::warning);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void registerEvents() {
|
||||
PluginManager pluginManager = this.getServer().getPluginManager();
|
||||
pluginManager.registerEvents(playerLoader, this);
|
||||
pluginManager.registerEvents(inventoryManager, this);
|
||||
pluginManager.registerEvents(new ContainerListener(accessor, languageManager), this);
|
||||
pluginManager.registerEvents(new ToggleListener(), this);
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
this.setCommandExecutor(new OpenInvCommand(this, config, inventoryManager, languageManager, playerLoader), "openinv", "openender");
|
||||
this.setCommandExecutor(new SearchContainerCommand(this, languageManager), "searchcontainer");
|
||||
this.setCommandExecutor(new SearchInvCommand(languageManager), "searchinv", "searchender");
|
||||
this.setCommandExecutor(new SearchEnchantCommand(languageManager), "searchenchant");
|
||||
|
||||
ContainerSettingCommand settingCommand = new ContainerSettingCommand(languageManager);
|
||||
for (PlayerToggle toggle : PlayerToggles.get()) {
|
||||
setCommandExecutor(settingCommand, toggle.getName().toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
}
|
||||
|
||||
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 (!accessor.isSupported()) {
|
||||
messageMethod.accept("Your server version (" + accessor.getVersion() + ") is not supported.");
|
||||
messageMethod.accept("Please download the correct version of OpenInv here: " + accessor.getReleasesLink());
|
||||
|
||||
// We check this property late so users can use jars that were remapped by Paper already.
|
||||
if (Boolean.getBoolean("paper.disable-plugin-rewriting")) {
|
||||
messageMethod.accept("OpenInv uses Spigot-mapped internals, but you have disabled plugin rewriting in Paper!");
|
||||
messageMethod.accept("Please set system property 'paper.disable-plugin-rewriting' to false.");
|
||||
}
|
||||
}
|
||||
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 config.isSaveDisabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableOfflineAccess() {
|
||||
return config.isOfflineDisabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean noArgsOpensSelf() {
|
||||
return config.doesNoArgsOpenSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IAnySilentContainer getAnySilentContainer() {
|
||||
return this.accessor.getAnySilentContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getAnyContainerStatus(@NotNull final OfflinePlayer offline) {
|
||||
return PlayerToggles.any().is(offline.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAnyContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) {
|
||||
PlayerToggles.any().set(offline.getUniqueId(), status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSilentContainerStatus(@NotNull final OfflinePlayer offline) {
|
||||
return PlayerToggles.silent().is(offline.getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSilentContainerStatus(@NotNull final OfflinePlayer offline, final boolean status) {
|
||||
PlayerToggles.silent().set(offline.getUniqueId(), status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online) {
|
||||
return inventoryManager.getEnderChest(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online) {
|
||||
return inventoryManager.getInventory(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
|
||||
Permissions edit = null;
|
||||
HumanEntity target = inventory.getPlayer();
|
||||
boolean ownContainer = player.equals(target);
|
||||
if (inventory instanceof ISpecialPlayerInventory) {
|
||||
edit = ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER;
|
||||
} else if (inventory instanceof ISpecialEnderChest) {
|
||||
edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_EDIT_OTHER;
|
||||
}
|
||||
|
||||
boolean viewOnly = edit != null && !edit.hasPermission(player);
|
||||
|
||||
if (ownContainer || viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY) {
|
||||
return this.accessor.openInventory(player, inventory, viewOnly);
|
||||
}
|
||||
|
||||
for (int level = 4; level > 0; --level) {
|
||||
String permission = "openinv.access.level." + level;
|
||||
// If the target doesn't have this access level...
|
||||
if (!target.hasPermission(permission)) {
|
||||
// If the viewer does have the access level, all good.
|
||||
if (player.hasPermission(permission)) {
|
||||
break;
|
||||
}
|
||||
// Otherwise check next access level.
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the viewer doesn't have an equal access level or equal access is a denial, deny.
|
||||
if (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Since this is a tie, setting decides view state.
|
||||
if (config.getAccessEqualMode() == AccessEqualMode.VIEW) {
|
||||
viewOnly = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return this.accessor.openInventory(player, inventory, viewOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayerLoaded(@NotNull UUID playerUuid) {
|
||||
return inventoryManager.getLoadedPlayer(playerUuid) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Player loadPlayer(@NotNull final OfflinePlayer offline) {
|
||||
return playerLoader.load(offline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable OfflinePlayer matchPlayer(@NotNull String name) {
|
||||
return playerLoader.match(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload(@NotNull final OfflinePlayer offline) {
|
||||
inventoryManager.unload(offline.getUniqueId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,107 +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.command;
|
||||
|
||||
import com.lishid.openinv.event.OpenEvents;
|
||||
import com.lishid.openinv.util.setting.PlayerToggle;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import com.lishid.openinv.util.setting.PlayerToggles;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ContainerSettingCommand implements TabExecutor {
|
||||
|
||||
private final @NotNull LanguageManager lang;
|
||||
|
||||
public ContainerSettingCommand(@NotNull LanguageManager lang) {
|
||||
this.lang = lang;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
lang.sendMessage(sender, "messages.error.consoleUnsupported");
|
||||
return true;
|
||||
}
|
||||
|
||||
PlayerToggle toggle = PlayerToggles.get(command.getName());
|
||||
|
||||
// Shouldn't be possible.
|
||||
if (toggle == null) {
|
||||
JavaPlugin.getProvidingPlugin(getClass()).getLogger().warning("Command /" + command.getName() + " registered with no corresponding toggle!");
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID playerId = player.getUniqueId();
|
||||
|
||||
if (args.length > 0) {
|
||||
args[0] = args[0].toLowerCase(Locale.ENGLISH);
|
||||
|
||||
if (args[0].equals("on")) {
|
||||
set(toggle, playerId, true);
|
||||
} else if (args[0].equals("off")) {
|
||||
set(toggle, playerId, false);
|
||||
} else if (!args[0].equals("check")) {
|
||||
// Invalid argument, show usage.
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
set(toggle, playerId, !toggle.is(playerId));
|
||||
}
|
||||
|
||||
String onOff = lang.getLocalizedMessage(player, toggle.is(playerId) ? "messages.info.on" : "messages.info.off");
|
||||
if (onOff == null) {
|
||||
onOff = String.valueOf(toggle.is(playerId));
|
||||
}
|
||||
|
||||
lang.sendMessage(
|
||||
sender,
|
||||
"messages.info.settingState",
|
||||
new Replacement("%setting%", toggle.getName()),
|
||||
new Replacement("%state%", onOff));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void set(@NotNull PlayerToggle toggle, @NotNull UUID uuid, boolean state) {
|
||||
if (toggle.set(uuid, state)) {
|
||||
OpenEvents.notifyPlayerToggle(toggle, uuid, state);
|
||||
}
|
||||
}
|
||||
|
||||
@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,249 +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.command;
|
||||
|
||||
import com.lishid.openinv.OpenInv;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.util.AccessEqualMode;
|
||||
import com.lishid.openinv.util.InventoryManager;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import com.lishid.openinv.util.PlayerLoader;
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import com.lishid.openinv.util.config.Config;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class OpenInvCommand implements TabExecutor {
|
||||
|
||||
private final @NotNull OpenInv plugin;
|
||||
private final @NotNull Config config;
|
||||
private final @NotNull InventoryManager manager;
|
||||
private final @NotNull LanguageManager lang;
|
||||
private final @NotNull PlayerLoader playerLoader;
|
||||
private final HashMap<Player, String> openInvHistory = new HashMap<>();
|
||||
private final HashMap<Player, String> openEnderHistory = new HashMap<>();
|
||||
|
||||
public OpenInvCommand(
|
||||
@NotNull OpenInv plugin,
|
||||
@NotNull Config config,
|
||||
@NotNull InventoryManager manager,
|
||||
@NotNull LanguageManager lang,
|
||||
@NotNull PlayerLoader playerLoader) {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.manager = manager;
|
||||
this.lang = lang;
|
||||
this.playerLoader = playerLoader;
|
||||
}
|
||||
|
||||
@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)) {
|
||||
lang.sendMessage(sender, "messages.error.consoleUnsupported");
|
||||
return true;
|
||||
}
|
||||
|
||||
String noArgValue;
|
||||
if (config.doesNoArgsOpenSelf()) {
|
||||
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 = playerLoader.match(name);
|
||||
|
||||
if (offlinePlayer == null || (!offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline())) {
|
||||
lang.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 (!config.isOfflineDisabled() && Permissions.ACCESS_OFFLINE.hasPermission(player)) {
|
||||
// Try loading the player's data
|
||||
onlineTarget = playerLoader.load(target);
|
||||
} else {
|
||||
lang.sendMessage(player, "messages.error.permissionPlayerOffline");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (Permissions.ACCESS_ONLINE.hasPermission(player)) {
|
||||
onlineTarget = target.getPlayer();
|
||||
} else {
|
||||
lang.sendMessage(player, "messages.error.permissionPlayerOnline");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (onlineTarget == null) {
|
||||
lang.sendMessage(player, "messages.error.invalidPlayer");
|
||||
return;
|
||||
}
|
||||
|
||||
// Permissions checks
|
||||
if (onlineTarget.equals(player)) {
|
||||
// Permission for opening own inventory.
|
||||
if (!(openinv ? Permissions.INVENTORY_OPEN_SELF : Permissions.ENDERCHEST_OPEN_SELF).hasPermission(player)) {
|
||||
lang.sendMessage(player, "messages.error.permissionOpenSelf");
|
||||
return;
|
||||
|
||||
}
|
||||
} else {
|
||||
// Permission for opening others' inventories.
|
||||
if (!(openinv ? Permissions.INVENTORY_OPEN_OTHER : Permissions.ENDERCHEST_OPEN_OTHER).hasPermission(player)) {
|
||||
lang.sendMessage(player, "messages.error.permissionOpenOther");
|
||||
return;
|
||||
}
|
||||
|
||||
// Protected check
|
||||
for (int level = 4; level > 0; --level) {
|
||||
String permission = "openinv.access.level." + level;
|
||||
if (onlineTarget.hasPermission(permission)
|
||||
&& (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY)) {
|
||||
lang.sendMessage(
|
||||
player,
|
||||
"messages.error.permissionExempt",
|
||||
new Replacement("%target%", onlineTarget.getDisplayName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Crossworld check
|
||||
if (!Permissions.ACCESS_CROSSWORLD.hasPermission(player)
|
||||
&& !onlineTarget.getWorld().equals(player.getWorld())) {
|
||||
lang.sendMessage(
|
||||
player,
|
||||
"messages.error.permissionCrossWorld",
|
||||
new Replacement("%target%", onlineTarget.getDisplayName()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.doesNoArgsOpenSelf()) {
|
||||
// Record the target
|
||||
(openinv ? this.openInvHistory : this.openEnderHistory).put(player, target.getUniqueId().toString());
|
||||
}
|
||||
|
||||
// Create the inventory
|
||||
final ISpecialInventory inv;
|
||||
try {
|
||||
inv = openinv ? manager.getInventory(onlineTarget) : manager.getEnderChest(onlineTarget);
|
||||
} catch (Exception e) {
|
||||
lang.sendMessage(player, "messages.error.commandException");
|
||||
plugin.getLogger().log(Level.WARNING, "Unable to create ISpecialInventory", e);
|
||||
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,145 +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.command;
|
||||
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
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.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Command for searching containers in a radius of chunks.
|
||||
*/
|
||||
public class SearchContainerCommand implements TabExecutor {
|
||||
|
||||
private final @NotNull Plugin plugin;
|
||||
private final @NotNull LanguageManager lang;
|
||||
|
||||
public SearchContainerCommand(@NotNull Plugin plugin, @NotNull LanguageManager lang) {
|
||||
this.plugin = plugin;
|
||||
this.lang = lang;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if (!(sender instanceof Player senderPlayer)) {
|
||||
lang.sendMessage(sender, "messages.error.consoleUnsupported");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args.length < 1) {
|
||||
// Must supply material
|
||||
return false;
|
||||
}
|
||||
|
||||
Material material = Material.matchMaterial(args[0]);
|
||||
|
||||
if (material == null) {
|
||||
lang.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(Locale.ENGLISH)).append(" (")
|
||||
.append(tileEntity.getX()).append(',').append(tileEntity.getY()).append(',')
|
||||
.append(tileEntity.getZ()).append("), ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Matches found, delete trailing comma and space
|
||||
if (!locations.isEmpty()) {
|
||||
locations.delete(locations.length() - 2, locations.length());
|
||||
} else {
|
||||
lang.sendMessage(
|
||||
sender,
|
||||
"messages.info.container.noMatches",
|
||||
new Replacement("%target%", material.name()));
|
||||
return true;
|
||||
}
|
||||
|
||||
lang.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,172 +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.command;
|
||||
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.Registry;
|
||||
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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* 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 @NotNull LanguageManager lang;
|
||||
|
||||
public SearchEnchantCommand(@NotNull LanguageManager lang) {
|
||||
this.lang = lang;
|
||||
}
|
||||
|
||||
@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) {
|
||||
// Not a level being specified.
|
||||
}
|
||||
|
||||
argument = argument.toLowerCase(Locale.ENGLISH);
|
||||
NamespacedKey key = NamespacedKey.fromString(argument);
|
||||
if (key == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Enchantment localEnchant = Registry.ENCHANTMENT.get(key);
|
||||
if (localEnchant != null) {
|
||||
enchant = localEnchant;
|
||||
}
|
||||
}
|
||||
|
||||
// Arguments not set correctly
|
||||
if (level == 0 && enchant == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StringBuilder players = new StringBuilder();
|
||||
for (Player player : Bukkit.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.isEmpty()) {
|
||||
// Matches found, delete trailing comma and space
|
||||
players.delete(players.length() - 2, players.length());
|
||||
} else {
|
||||
lang.sendMessage(
|
||||
sender,
|
||||
"messages.info.player.noMatches",
|
||||
new Replacement("%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level));
|
||||
return true;
|
||||
}
|
||||
|
||||
lang.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(), Registry.ENCHANTMENT.stream().toArray(Enchantment[]::new));
|
||||
} else {
|
||||
return TabCompleter.completeInteger(args[1]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.command;
|
||||
|
||||
import com.lishid.openinv.util.TabCompleter;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import com.lishid.openinv.util.lang.Replacement;
|
||||
import org.bukkit.Bukkit;
|
||||
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.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SearchInvCommand implements TabExecutor {
|
||||
|
||||
private final @NotNull LanguageManager lang;
|
||||
|
||||
public SearchInvCommand(@NotNull LanguageManager lang) {
|
||||
this.lang = lang;
|
||||
}
|
||||
|
||||
@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.matchMaterial(args[0]);
|
||||
}
|
||||
|
||||
if (material == null) {
|
||||
lang.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) {
|
||||
lang.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 : Bukkit.getServer().getOnlinePlayers()) {
|
||||
Inventory inventory = searchInv ? player.getInventory() : player.getEnderChest();
|
||||
int total = 0;
|
||||
for (ItemStack itemStack : inventory.getContents()) {
|
||||
if (itemStack != null && itemStack.getType() == material) {
|
||||
total += itemStack.getAmount();
|
||||
if (total >= count) {
|
||||
players.append(player.getName()).append(", ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Matches found, delete trailing comma and space
|
||||
if (!players.isEmpty()) {
|
||||
players.delete(players.length() - 2, players.length());
|
||||
} else {
|
||||
lang.sendMessage(
|
||||
sender,
|
||||
"messages.info.player.noMatches",
|
||||
new Replacement("%target%", material.name()));
|
||||
return true;
|
||||
}
|
||||
|
||||
lang.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,144 +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.listener;
|
||||
|
||||
import com.google.errorprone.annotations.Keep;
|
||||
import com.lishid.openinv.internal.ViewOnly;
|
||||
import com.lishid.openinv.util.InternalAccessor;
|
||||
import com.lishid.openinv.util.Permissions;
|
||||
import com.lishid.openinv.util.setting.PlayerToggles;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
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.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryDragEvent;
|
||||
import org.bukkit.event.inventory.InventoryInteractEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A listener managing AnyContainer, SilentContainer, and more.
|
||||
*/
|
||||
public class ContainerListener implements Listener {
|
||||
|
||||
private final @NotNull InternalAccessor accessor;
|
||||
private final @NotNull LanguageManager lang;
|
||||
|
||||
public ContainerListener(@NotNull InternalAccessor accessor, @NotNull LanguageManager lang) {
|
||||
this.accessor = accessor;
|
||||
this.lang = lang;
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
private void onPlayerInteract(@NotNull PlayerInteractEvent event) {
|
||||
// Ignore events from other plugins.
|
||||
if (!PlayerInteractEvent.class.equals(event.getClass())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getAction() != Action.RIGHT_CLICK_BLOCK
|
||||
|| event.getPlayer().isSneaking()
|
||||
|| event.useInteractedBlock() == Result.DENY
|
||||
|| event.getClickedBlock() == null
|
||||
|| !accessor.getAnySilentContainer().isAnySilentContainer(event.getClickedBlock())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = event.getPlayer();
|
||||
UUID playerId = player.getUniqueId();
|
||||
boolean any = Permissions.CONTAINER_ANY.hasPermission(player) && PlayerToggles.any().is(playerId);
|
||||
boolean needsAny = accessor.getAnySilentContainer().isAnyContainerNeeded(event.getClickedBlock());
|
||||
|
||||
if (!any && needsAny) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean silent = Permissions.CONTAINER_SILENT.hasPermission(player) && PlayerToggles.silent().is(playerId);
|
||||
|
||||
// If anycontainer or silentcontainer is active
|
||||
if (any || silent) {
|
||||
if (accessor.getAnySilentContainer().activateContainer(player, silent, event.getClickedBlock())) {
|
||||
if (silent && needsAny) {
|
||||
lang.sendSystemMessage(player, "messages.info.containerBlockedSilent");
|
||||
} else if (needsAny) {
|
||||
lang.sendSystemMessage(player, "messages.info.containerBlocked");
|
||||
} else if (silent) {
|
||||
lang.sendSystemMessage(player, "messages.info.containerSilent");
|
||||
}
|
||||
}
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler
|
||||
private void onInventoryClose(@NotNull final InventoryCloseEvent event) {
|
||||
if (!(event.getPlayer() instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
InventoryHolder holder = event.getInventory().getHolder();
|
||||
if (PlayerToggles.silent().is(player.getUniqueId())
|
||||
&& holder != null
|
||||
&& this.accessor.getAnySilentContainer().isAnySilentContainer(holder)) {
|
||||
this.accessor.getAnySilentContainer().deactivateContainer(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
private void onInventoryClick(@NotNull final InventoryClickEvent event) {
|
||||
handleInventoryInteract(event);
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
private void onInventoryDrag(@NotNull final InventoryDragEvent event) {
|
||||
handleInventoryInteract(event);
|
||||
}
|
||||
|
||||
private void handleInventoryInteract(@NotNull final InventoryInteractEvent event) {
|
||||
HumanEntity entity = event.getWhoClicked();
|
||||
|
||||
// Un-cancel spectator interactions.
|
||||
if (entity.getGameMode() == GameMode.SPECTATOR && Permissions.SPECTATE_CLICK.hasPermission(entity)) {
|
||||
event.setCancelled(false);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Inventory inventory = event.getView().getTopInventory();
|
||||
if (inventory instanceof ViewOnly) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.lishid.openinv.listener;
|
||||
|
||||
import com.google.errorprone.annotations.Keep;
|
||||
import com.lishid.openinv.util.setting.PlayerToggles;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ToggleListener implements Listener {
|
||||
|
||||
@Keep
|
||||
@EventHandler
|
||||
private void onPlayerQuit(@NotNull PlayerQuitEvent event) {
|
||||
UUID playerId = event.getPlayer().getUniqueId();
|
||||
PlayerToggles.any().set(playerId, false);
|
||||
PlayerToggles.silent().set(playerId, false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public enum AccessEqualMode {
|
||||
|
||||
DENY, ALLOW, VIEW;
|
||||
|
||||
public static @NotNull AccessEqualMode of(@Nullable String value) {
|
||||
if (value == null) {
|
||||
return VIEW;
|
||||
}
|
||||
return switch (value.toLowerCase(Locale.ENGLISH)) {
|
||||
case "deny", "false" -> DENY;
|
||||
case "allow", "true" -> ALLOW;
|
||||
default -> VIEW;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,219 +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.util;
|
||||
|
||||
import com.github.jikoo.planarwrappers.util.version.BukkitVersions;
|
||||
import com.github.jikoo.planarwrappers.util.version.Version;
|
||||
import com.lishid.openinv.internal.Accessor;
|
||||
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.internal.PlayerManager;
|
||||
import com.lishid.openinv.util.lang.LanguageManager;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class InternalAccessor {
|
||||
|
||||
private @Nullable Accessor internal;
|
||||
|
||||
public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
|
||||
|
||||
try {
|
||||
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 4))) {
|
||||
internal = new com.lishid.openinv.internal.v1_21_R3.InternalAccessor(logger, lang);
|
||||
} else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 3))) {
|
||||
internal = new com.lishid.openinv.internal.v1_21_R2.InternalAccessor(logger, lang);
|
||||
}
|
||||
if (internal != null) {
|
||||
InventoryAccess.setProvider(internal::get);
|
||||
}
|
||||
} catch (NoClassDefFoundError | Exception e) {
|
||||
internal = null;
|
||||
InventoryAccess.setProvider(null);
|
||||
}
|
||||
}
|
||||
|
||||
public String getReleasesLink() {
|
||||
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 4, 4))) { // Good luck.
|
||||
return "https://dev.bukkit.org/projects/openinv/files?&sort=datecreated";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 8, 8))) { // 1.8.8
|
||||
return "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 13))) { // 1.4.4+ had versioned packages.
|
||||
return "https://github.com/lishid/OpenInv/releases/tag/4.0.0 (OpenInv-legacy)";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 13))) { // 1.13
|
||||
return "https://github.com/lishid/OpenInv/releases/tag/4.0.0";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 14))) { // 1.13.1, 1.13.2
|
||||
return "https://github.com/lishid/OpenInv/releases/tag/4.0.7";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 14))) { // 1.14 to 1.14.1 had no revision bump.
|
||||
return "https://github.com/lishid/OpenInv/releases/tag/4.0.0";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 14, 1))) { // 1.14.1 to 1.14.2 had no revision bump.
|
||||
return "https://github.com/lishid/OpenInv/releases/tag/4.0.1";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 15))) { // 1.14.2
|
||||
return "https://github.com/lishid/OpenInv/releases/tag/4.1.1";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 15, 1))) { // 1.15, 1.15.1
|
||||
return "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 16))) { // 1.15.2
|
||||
return "https://github.com/Jikoo/OpenInv/commit/502f661be39ee85d300851dd571f3da226f12345 (never released)";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 16, 1))) { // 1.16, 1.16.1
|
||||
return "https://github.com/lishid/OpenInv/releases/tag/4.1.4";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 16, 3))) { // 1.16.2, 1.16.3
|
||||
return "https://github.com/lishid/OpenInv/releases/tag/4.1.5";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 17))) { // 1.16.4, 1.16.5
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.8";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 18, 1))) { // 1.17, 1.18, 1.18.1
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/4.1.10";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 19))) { // 1.18.2
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 19))) { // 1.19
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/4.2.0";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 19, 1))) { // 1.19.1
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/4.2.2";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 19, 3))) { // 1.19.2, 1.19.3
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/4.3.0";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 20))) { // 1.19.4
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 1))) { // 1.20, 1.20.1
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.1";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 20, 3))) { // 1.20.2, 1.20.3
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/4.4.3";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 5))) { // 1.20.5
|
||||
return "Unsupported; upgrade to 1.20.6: https://github.com/Jikoo/OpenInv/releases/tag/5.1.2";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21))) { // 1.20.4, 1.20.6, 1.21
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.2";
|
||||
}
|
||||
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 2))) {
|
||||
return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.3";
|
||||
}
|
||||
return "https://github.com/Jikoo/OpenInv/releases";
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload internal features.
|
||||
*/
|
||||
public void reload(ConfigurationSection config) {
|
||||
if (internal != null) {
|
||||
internal.reload(config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server implementation version.
|
||||
*
|
||||
* @return the version
|
||||
*/
|
||||
public @NotNull String getVersion() {
|
||||
return BukkitVersions.MINECRAFT.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the server implementation is supported.
|
||||
*
|
||||
* @return true if initialized for a supported server version
|
||||
*/
|
||||
public boolean isSupported() {
|
||||
return internal != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 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 (internal == null) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT));
|
||||
}
|
||||
return internal.getAnySilentContainer();
|
||||
}
|
||||
|
||||
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory, boolean viewOnly) {
|
||||
if (internal == null) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT));
|
||||
}
|
||||
return internal.getPlayerManager().openInventory(player, inventory, viewOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the instance of the IPlayerDataManager implementation for the current server version.
|
||||
*
|
||||
* @return the IPlayerDataManager
|
||||
* @throws IllegalStateException if server version is unsupported
|
||||
*/
|
||||
@NotNull PlayerManager getPlayerDataManager() {
|
||||
if (internal == null) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT));
|
||||
}
|
||||
return internal.getPlayerManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the ISpecialEnderChest implementation for the given Player.
|
||||
*
|
||||
* @param player the Player
|
||||
* @return the ISpecialEnderChest created
|
||||
*/
|
||||
ISpecialEnderChest createEnderChest(final Player player) {
|
||||
if (internal == null) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT));
|
||||
}
|
||||
return internal.createEnderChest(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the ISpecialPlayerInventory implementation for the given Player.
|
||||
*
|
||||
* @param player the Player
|
||||
* @return the ISpecialPlayerInventory created
|
||||
*/
|
||||
ISpecialPlayerInventory createInventory(final Player player) {
|
||||
if (internal == null) {
|
||||
throw new IllegalStateException(String.format("Unsupported server version %s!", BukkitVersions.MINECRAFT));
|
||||
}
|
||||
return internal.createPlayerInventory(player);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,280 +0,0 @@
|
||||
package com.lishid.openinv.util;
|
||||
|
||||
import com.github.jikoo.planarwrappers.util.version.BukkitVersions;
|
||||
import com.github.jikoo.planarwrappers.util.version.Version;
|
||||
import com.google.errorprone.annotations.Keep;
|
||||
import com.lishid.openinv.event.OpenEvents;
|
||||
import com.lishid.openinv.internal.ISpecialEnderChest;
|
||||
import com.lishid.openinv.internal.ISpecialInventory;
|
||||
import com.lishid.openinv.internal.ISpecialPlayerInventory;
|
||||
import com.lishid.openinv.util.config.Config;
|
||||
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.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryOpenEvent;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A manager for special inventories. Delegates creation and tracks copies in use.
|
||||
*/
|
||||
public class InventoryManager implements Listener {
|
||||
|
||||
private final Map<UUID, ISpecialPlayerInventory> inventories = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, ISpecialEnderChest> enderChests = new ConcurrentHashMap<>();
|
||||
private final Set<UUID> expectedCloses = new HashSet<>();
|
||||
private final @NotNull Plugin plugin;
|
||||
private final @NotNull Config config;
|
||||
private final @NotNull InternalAccessor accessor;
|
||||
|
||||
public InventoryManager(@NotNull Plugin plugin, @NotNull Config config, @NotNull InternalAccessor accessor) {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.accessor = accessor;
|
||||
}
|
||||
|
||||
public void evictAll() {
|
||||
Stream.concat(inventories.values().stream(), enderChests.values().stream())
|
||||
.map(inventory -> {
|
||||
// Rather than iterate twice, evict all viewers during remapping.
|
||||
for (HumanEntity viewer : List.copyOf(inventory.getBukkitInventory().getViewers())) {
|
||||
expectedCloses.add(viewer.getUniqueId());
|
||||
viewer.closeInventory();
|
||||
}
|
||||
// If saving is prevented, return a null value for the player to save.
|
||||
if (config.isSaveDisabled() || OpenEvents.saveCancelled(inventory)) {
|
||||
return null;
|
||||
}
|
||||
if (inventory.getPlayer() instanceof Player player) {
|
||||
return player;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.forEach(player -> {
|
||||
if (!player.isOnline()) {
|
||||
accessor.getPlayerDataManager().inject(player).saveData();
|
||||
}
|
||||
});
|
||||
inventories.clear();
|
||||
enderChests.clear();
|
||||
expectedCloses.clear();
|
||||
}
|
||||
|
||||
public @NotNull ISpecialPlayerInventory getInventory(@NotNull Player player) {
|
||||
return inventories.computeIfAbsent(player.getUniqueId(), uuid -> accessor.createInventory(player));
|
||||
}
|
||||
|
||||
public @NotNull ISpecialEnderChest getEnderChest(@NotNull Player player) {
|
||||
return enderChests.computeIfAbsent(player.getUniqueId(), uuid -> accessor.createEnderChest(player));
|
||||
}
|
||||
|
||||
public @Nullable Player getLoadedPlayer(@NotNull UUID uuid) {
|
||||
ISpecialInventory inUse = inventories.get(uuid);
|
||||
if (inUse != null) {
|
||||
return (Player) inUse.getPlayer();
|
||||
}
|
||||
inUse = enderChests.get(uuid);
|
||||
if (inUse != null) {
|
||||
return (Player) inUse.getPlayer();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void unload(@NotNull UUID uuid) {
|
||||
inventories.computeIfPresent(uuid, this::remove);
|
||||
enderChests.computeIfPresent(uuid, this::remove);
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
private void onPlayerJoin(@NotNull PlayerJoinEvent event) {
|
||||
consumeLoaded(event.getPlayer().getUniqueId(), inventory -> {
|
||||
inventory.setPlayerOnline(event.getPlayer());
|
||||
checkViewerAccess(inventory, true);
|
||||
});
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
private void onPlayerQuit(@NotNull PlayerQuitEvent event) {
|
||||
consumeLoaded(event.getPlayer().getUniqueId(), inventory -> {
|
||||
inventory.setPlayerOffline();
|
||||
checkViewerAccess(inventory, false);
|
||||
});
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler
|
||||
private void onWorldChanged(@NotNull PlayerChangedWorldEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
consumeLoaded(player.getUniqueId(), inventory -> checkViewerAccess(inventory, player.isOnline()));
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler
|
||||
private void onInventoryClose(@NotNull InventoryCloseEvent event) {
|
||||
ISpecialInventory inventory = InventoryAccess.getInventory(event.getInventory());
|
||||
|
||||
// If this is not an ISpecialInventory or the inventory was closed elsewhere internally, don't handle.
|
||||
if (inventory == null || expectedCloses.remove(event.getPlayer().getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch the active ISpecialInventory of this type.
|
||||
Map<UUID, ? extends ISpecialInventory> map = inventory instanceof ISpecialPlayerInventory ? inventories : enderChests;
|
||||
UUID key = inventory.getPlayer().getUniqueId();
|
||||
ISpecialInventory loaded = map.get(key);
|
||||
|
||||
// If there is no loaded inventory, it has already been removed and saved.
|
||||
if (loaded == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This should only be possible if a plugin is going to extreme lengths to mess with our inventories.
|
||||
if (loaded != inventory) {
|
||||
// Immediately remove affected inventory, then dump all viewers. We don't want to risk duplication bugs.
|
||||
map.remove(key);
|
||||
remove(key, loaded);
|
||||
remove(key, inventory);
|
||||
// The loaded one is "correct" as far as we're concerned, so save that.
|
||||
save(loaded);
|
||||
}
|
||||
|
||||
// Schedule task to check in use status later this tick. Closing user is still in viewer list.
|
||||
plugin.getServer().getScheduler().runTask(plugin, () -> {
|
||||
if (loaded.isInUse()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-fetch from map to reduce odds of a duplicate save.
|
||||
ISpecialInventory current = map.remove(key);
|
||||
|
||||
if (current != null) {
|
||||
save(current);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Keep
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
private void onInventoryOpen(@NotNull InventoryOpenEvent event) {
|
||||
ISpecialInventory inventory = InventoryAccess.getInventory(event.getInventory());
|
||||
if (inventory == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<UUID, ? extends ISpecialInventory> map = inventory instanceof ISpecialPlayerInventory ? inventories : enderChests;
|
||||
UUID key = inventory.getPlayer().getUniqueId();
|
||||
ISpecialInventory loaded = map.get(key);
|
||||
|
||||
if (!inventory.equals(loaded)) {
|
||||
event.setCancelled(true);
|
||||
plugin.getLogger().log(
|
||||
Level.WARNING,
|
||||
"Prevented a plugin from opening an untracked ISpecialInventory!",
|
||||
new Throwable("Untracked ISpecialInventory"));
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends ISpecialInventory> void checkViewerAccess(@NotNull T inventory, boolean online) {
|
||||
|
||||
Player owner = (Player) inventory.getPlayer();
|
||||
Permissions connectedState = online ? Permissions.ACCESS_ONLINE : Permissions.ACCESS_OFFLINE;
|
||||
boolean alwaysDenied = !online && config.isOfflineDisabled();
|
||||
|
||||
// Copy viewers so we don't modify the list we're iterating over when closing inventories.
|
||||
List<HumanEntity> viewers = new ArrayList<>(inventory.getBukkitInventory().getViewers());
|
||||
// Legacy: Owner is always a viewer of own inventory.
|
||||
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) {
|
||||
if (inventory instanceof ISpecialPlayerInventory) {
|
||||
Inventory active = owner.getOpenInventory().getTopInventory();
|
||||
if (!active.equals(inventory.getBukkitInventory())) {
|
||||
viewers.remove(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (HumanEntity viewer : viewers) {
|
||||
if (alwaysDenied
|
||||
|| !connectedState.hasPermission(viewer)
|
||||
|| (!Objects.equals(owner.getWorld(), viewer.getWorld()) && !Permissions.ACCESS_CROSSWORLD.hasPermission(viewer))) {
|
||||
expectedCloses.add(viewer.getUniqueId());
|
||||
viewer.closeInventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void consumeLoaded(@NotNull UUID key, @NotNull Consumer<@NotNull ISpecialInventory> consumer) {
|
||||
boolean saved = consumeLoaded(inventories, key, false, consumer);
|
||||
consumeLoaded(enderChests, key, saved, consumer);
|
||||
}
|
||||
|
||||
private <T extends ISpecialInventory> boolean consumeLoaded(
|
||||
@NotNull Map<UUID, T> map,
|
||||
@NotNull UUID key,
|
||||
boolean saved,
|
||||
@NotNull Consumer<@NotNull ISpecialInventory> consumer) {
|
||||
T inventory = map.get(key);
|
||||
|
||||
if (inventory == null) {
|
||||
return saved;
|
||||
}
|
||||
|
||||
consumer.accept(inventory);
|
||||
if (!inventory.isInUse()) {
|
||||
map.remove(key);
|
||||
|
||||
if (!saved) {
|
||||
save(inventory);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return saved;
|
||||
}
|
||||
|
||||
private void save(@NotNull ISpecialInventory inventory) {
|
||||
if (config.isSaveDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = (Player) inventory.getPlayer();
|
||||
|
||||
if (!player.isOnline() && !OpenEvents.saveCancelled(inventory)) {
|
||||
accessor.getPlayerDataManager().inject(player).saveData();
|
||||
}
|
||||
}
|
||||
|
||||
@Contract("_, _ -> null")
|
||||
private <T extends ISpecialInventory> @Nullable T remove(@NotNull UUID key, @NotNull T inventory) {
|
||||
for (HumanEntity viewer : List.copyOf(inventory.getBukkitInventory().getViewers())) {
|
||||
expectedCloses.add(viewer.getUniqueId());
|
||||
viewer.closeInventory();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
Loaded 100 of 271 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user