Compare commits

...
54 Commits
Author SHA1 Message Date
Jikoo d564773be6 Bump version to 5.1.9 for release 2025-02-14 11:33:31 -05:00
Adam e834e940e2 Convert to precompiled task (#289) 2025-02-14 01:02:24 -05:00
Jikoo cd73430bb2 Try to resolve remote cache issues
The weird part is that I don't have any of these problems locally. Remote is executing tasks in a really weird order, common adapter is building after Spigot adapter despite Spigot adapter depending on an output from it.
May just have to disable caching, but would like to avoid that if possible.
2025-02-13 12:19:03 -05:00
Jikoo 1c1dc9505b Bump version to 5.1.9-SNAPSHOT for development 2025-02-13 11:49:04 -05:00
Jikoo 1a76e6fd57 Bump version to 5.1.8 for release 2025-02-13 11:48:59 -05:00
Jikoo 48a61e6043 Update release script
Now I can do things like have unstaged changes on the master branch when I cut a release! Not good!
2025-02-13 11:48:47 -05:00
Adam 4de2c2fe6d Code health (#288)
* Fix some missing nullity/deprecation annotations
* Remove unnecessary Paper checks
* Respect title changes from InventoryOpenEvent
2025-02-13 16:08:59 +00:00
Adam e4fdd613ae Expand support back to Paper 1.21.1 (#287) 2025-02-12 13:01:53 +00:00
Jikoo 9f50de5dfe Unify Spigot dependency version information declaration
May be worth adding an entry for latest MC version that is shared between common Paper and current Spigot modules.
2025-02-11 10:44:25 -05:00
dependabot[bot] e9d63f31ed Bump com.gradleup.shadow from 8.3.5 to 8.3.6 (#285) 2025-02-10 03:45:09 +00:00
Jikoo 73e003bde4 Update dependabot ecosystem 2025-02-09 22:40:28 -05:00
Jikoo 04ed652073 Bump version to 5.1.8-SNAPSHOT for development 2025-02-09 22:38:34 -05:00
Jikoo 49e408ee7a Bump version to 5.1.7 for release 2025-02-09 22:38:13 -05:00
Jikoo 0df5e39a6d Correct release notes
Paper 1.21.3 requires changes to placeholders and is not currently supported.
2025-02-09 22:37:37 -05:00
Adam 7ab3e0c03f Compile against Paper (#282)
* Convert to Gradle
* Reduce script usage
* Simplify release process
2025-02-09 22:15:37 -05:00
dependabot[bot] 348fde9173 Bump org.jetbrains:annotations from 26.0.1 to 26.0.2 (#283) 2025-02-01 17:35:05 +00:00
Jikoo 6c69350c30 Bump version to 5.1.7-SNAPSHOT for development 2025-01-18 13:58:16 -05:00
Jikoo 19eafddfac Bump version to 5.1.6 for release 2025-01-18 13:57:48 -05:00
Adam 8a388b5620 Fix view-only ender chest (#278)
Fixes access permissions being checked against self
2025-01-18 17:45:44 +00:00
Adam 206fff2c0a Fix off-hand update relying on Spigot method (#277) 2025-01-18 11:56:22 -05:00
Jikoo 6656e431b6 Bump version to 5.1.6-SNAPSHOT for development 2025-01-10 11:02:10 -05:00
Jikoo f8df83b31d Bump version to 5.1.5 for release 2025-01-10 11:01:37 -05:00
Jikoo eb98bd79d3 Update release action 2025-01-10 11:01:18 -05:00
Adam e948fdc011 Improve compatibility of view-only inventories (#273) 2025-01-10 10:35:48 -05:00
Jikoo f5c31c32d3 Correct edit other permission
See discussion #266
2025-01-04 21:37:29 -05:00
dependabot[bot] 56f8cf4d71 Bump softprops/action-gh-release from 2.1.0 to 2.2.0 (#271)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.1.0 to 2.2.0.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2.1.0...v2.2.0)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-01 13:11:50 -05:00
dependabot[bot] 7f1f6ccc82 Bump com.github.jikoo:planarwrappers from 3.2.3 to 3.3.0 (#270) 2025-01-01 17:39:40 +00:00
Adam 9196ab3e61 Improve equal access (#269)
* Respect deny equals mode if viewer cannot edit
* Revert default mode to allow equals
2024-12-29 13:47:09 +00:00
Adam 0ac7575bad Re-add rpack support for 1.21.3 (#264)
* Re-add support for 1.21.3 to resource pack
* Add pack icon
2024-12-14 13:18:44 -05:00
Jikoo b0c7c8421e Bump version to 5.1.5-SNAPSHOT for development 2024-12-08 20:09:37 -05:00
Jikoo 9b54e4e22e Bump version to 5.1.4 for release 2024-12-08 20:08:52 -05:00
Adam 5ac09bc5a3 Add support for 1.21.4 (#262)
* Add 1.21.4 adapter
* Update resource pack to 1.21.4
2024-12-08 20:07:34 -05:00
Jikoo 4e89a04b36 Correct header 2024-12-02 00:26:07 -05:00
dependabot[bot]andJikoo 73a9c743a6 Bump com.google.errorprone:error_prone_core from 2.35.1 to 2.36.0 (#260)
* Bump com.google.errorprone:error_prone_core from 2.35.1 to 2.36.0

Bumps [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) from 2.35.1 to 2.36.0.
- [Release notes](https://github.com/google/error-prone/releases)
- [Commits](https://github.com/google/error-prone/compare/v2.35.1...v2.36.0)

---
updated-dependencies:
- dependency-name: com.google.errorprone:error_prone_core
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Add new required arg

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jikoo <jikoo.games@gmail.com>
2024-12-02 05:10:31 +00:00
dependabot[bot] 8d897664ef Bump softprops/action-gh-release from 2.0.9 to 2.1.0 (#259)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.9 to 2.1.0.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2.0.9...v2.1.0)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-01 12:27:53 -05:00
Christoffer Tibell 03ea798ee0 Add support for 1.21.3 to legibility pack (#258) 2024-11-01 18:39:01 -04:00
dependabot[bot] 08d4a0ea27 Bump softprops/action-gh-release from 2.0.8 to 2.0.9 (#257)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.8 to 2.0.9.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2.0.8...v2.0.9)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-01 18:38:06 -04:00
dependabot[bot] cd7219f472 Bump com.google.errorprone:error_prone_core from 2.33.0 to 2.35.1 (#256) 2024-11-01 17:18:22 +00:00
dependabot[bot] 532dffc068 Bump org.apache.maven.plugins:maven-dependency-plugin (#255) 2024-11-01 17:17:31 +00:00
Jikoo 6df4de1dd3 Bump version to 5.1.4-SNAPSHOT for development 2024-10-29 10:53:32 -04:00
Jikoo df913e33c3 Bump version to 5.1.3 for release 2024-10-29 10:52:51 -04:00
Adam 21f6b005cd Add support for 1.21.3 (#254)
* Add support for 1.21.3
* Drop 1.20.4, 1.20.6, 1.21.0
   * 1.20.0 is incompatible with the final changes made to inventories in 1.21.1
2024-10-29 10:51:24 -04:00
dependabot[bot] d2ce3a542a Bump pascalgn/automerge-action from 0.16.3 to 0.16.4 (#250)
Bumps [pascalgn/automerge-action](https://github.com/pascalgn/automerge-action) from 0.16.3 to 0.16.4.
- [Release notes](https://github.com/pascalgn/automerge-action/releases)
- [Commits](https://github.com/pascalgn/automerge-action/compare/v0.16.3...v0.16.4)

---
updated-dependencies:
- dependency-name: pascalgn/automerge-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-01 20:23:16 -04:00
dependabot[bot] 2c51423882 Bump org.jetbrains:annotations from 24.1.0 to 25.0.0 (#249) 2024-10-01 17:31:48 +00:00
dependabot[bot] b8636cd385 Bump com.google.errorprone:error_prone_core from 2.31.0 to 2.33.0 (#248) 2024-10-01 17:31:07 +00:00
Boy0000 46b8b0c928 Fix resolutions for items so mipmap level isnt lowered (#247)
* fix resolutions for items so mipmap level isnt lowered

* adjust crafting_output texture slightly
2024-09-15 20:00:49 -04:00
Jikoo 8229d5f0d9 Bump version to 5.1.3-SNAPSHOT for development 2024-09-11 11:50:30 -04:00
Jikoo c6d36cc9cf Bump version to 5.1.2 for release 2024-09-11 11:49:58 -04:00
Jikoo 54f078724f also quits
Boy, and I thought having to make the first commit was embarrassing, wait till people see that I realized that was missing and didn't think about the counterpart. At least I did think about it though, I wasn't even testing on 1.20 where it would break things.
2024-09-11 11:42:04 -04:00
Jikoo 847f22f63f Fix containers not being notified when owner joins 2024-09-11 11:33:30 -04:00
dependabot[bot] f121c113f0 Bump org.apache.maven.plugins:maven-dependency-plugin (#243) 2024-09-01 17:31:41 +00:00
dependabot[bot] 8d60cd0726 Bump com.google.errorprone:error_prone_core from 2.29.2 to 2.31.0 (#244) 2024-09-01 17:31:34 +00:00
Jikoo 7b504971b1 Use Java 21 and Maven 3.9.8 for JitPack
Closes #242
2024-08-12 00:22:53 -04:00
Jikoo c1a20a4e24 Bump version to 5.1.2-SNAPSHOT for development 2024-08-10 13:33:43 -04:00
144 changed files with 2978 additions and 5392 deletions

No files matched your search

+1 -1
View File
@@ -4,7 +4,7 @@ updates:
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "maven"
- package-ecosystem: "gradle"
directory: "/"
schedule:
interval: "monthly"
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
github-token: "${{ secrets.GITHUB_TOKEN }}"
pull-request-number: "${{ env.PR_NUMBER }}"
- name: Merge
uses: pascalgn/automerge-action@v0.16.3
uses: pascalgn/automerge-action@v0.16.4
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
MERGE_LABELS: "dependencies,java"
+9 -14
View File
@@ -7,7 +7,7 @@ on:
tags-ignore:
- '**'
paths-ignore:
- resource-pack/**
- resource-pack/openinv-legibility-pack/**
# Enable running CI via other Actions, i.e. for drafting releases and handling PRs.
workflow_call:
@@ -21,14 +21,15 @@ jobs:
with:
distribution: 'temurin'
java-version: '21'
cache: 'maven'
# Install Spigot dependencies if necessary.
- name: Install Spigot Dependencies
run: . scripts/install_spigot_dependencies.sh
# TODO convert to a Gradle plugin later for portability
- name: Install Spigot dependency
run: ./scripts/install_spigot.sh 1.21.4
- name: Build With Maven
run: mvn -e clean package -am -P all
- uses: gradle/actions/setup-gradle@v4
- name: Build with Gradle
run: ./gradlew clean build
# Upload artifacts
- name: Upload Distributable Jar
@@ -36,10 +37,4 @@ jobs:
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
path: ./dist/*
+13 -16
View File
@@ -12,34 +12,31 @@ jobs:
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.0.8
uses: softprops/action-gh-release@v2.2.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: ${{ env.VERSIONED_NAME }}
body: ${{ env.GENERATED_CHANGELOG }}
# May actually want to manually specify Paper versions all the time; they won't all
# need to be specified in code thanks to Mojang-mapped server internals.
# A bit annoying, but not terribly so.
# Instead, fetch min and max (which will also be Spigot version) and manually list intermediate versions?
body: |-
## Supported server versions
### Paper
TODO COMMA-SEPARATED VERSIONS
### Spigot
TODO VERSION
TODO HELLO HUMAN, PRESS THE GENERATE CHANGELOG BUTTON PLEASE.
draft: true
prerelease: false
files: ./dist/**
+1 -1
View File
@@ -34,4 +34,4 @@ jobs:
game_versions: "${{ env.CURSEFORGE_MINECRAFT_VERSIONS }}"
release_type: release
changelog_type: markdown
changelog: "${{ env.CURSEFORGE_CHANGELOG }}"
changelog: "${{ github.event.release.body }}"
+2 -2
View File
@@ -7,7 +7,7 @@ on:
tags-ignore:
- '**'
paths:
- resource-pack/**
- resource-pack/openinv-legibility-pack/**
jobs:
build:
@@ -20,5 +20,5 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: openinv-legibility-pack
path: ./resource-pack/
path: ./resource-pack/openinv-legibility-pack/
compression-level: 9
+3
View File
@@ -2,9 +2,12 @@
**/.project
**/.classpath
**/.idea/
**/.gradle/
**.iml
**/target/
**/bin/
**/out/
**/build/
**/dist/
**/dependency-reduced-pom.xml
**/pom.xml.versionsBackup
+7 -10
View File
@@ -36,22 +36,19 @@ The OpenInv API is available via [JitPack](https://jitpack.io/).
```xml
<dependencies>
<dependency>
<groupId>com.github.jikoo.OpenInv</groupId>
<artifactId>openinvapi</artifactId>
<groupId>com.github.Jikoo</groupId>
<artifactId>OpenInv</artifactId>
<version>${openinv.version}</version>
</dependency>
</dependencies>
```
Note that since JitPack only builds the API now, the "full" OpenInv jar on JitPack is actually the openinvapi artifact. This is a change from previous dependency declaration that I hope to revert.
### Compilation
To compile, the relevant Spigot jars must be installed in the local repository.
To compile, the relevant Spigot jar 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`
`$serverVersion` is the version of the server, i.e. `1.21.4`
To compile just the API, execute Maven as usual:
`mvn clean package`
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).
Once Spigot is installed, execute the gradle wrapper:
`./gradlew build`
+21
View File
@@ -0,0 +1,21 @@
plugins {
`openinv-base`
}
dependencies {
implementation(project(":openinvapi"))
}
tasks.processResources {
expand("version" to version)
}
tasks.register<Copy>("distributeAddons") {
into(rootProject.layout.projectDirectory.dir("dist"))
from(tasks.jar)
rename("openinvtogglepersist.*\\.jar", "OITogglePersist.jar")
}
tasks.assemble {
dependsOn(tasks.named("distributeAddons"))
}
-72
View File
@@ -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.1</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>
@@ -1,6 +1,6 @@
name: OITogglePersist
main: com.github.jikoo.openinv.togglepersist.TogglePersist
version: ${project.version}
version: ${version}
author: Jikoo
description: An OpenInv addon allowing /anycontainer and /silentcontainer to persist across sessions.
api-version: "1.20"
+13
View File
@@ -0,0 +1,13 @@
plugins {
`openinv-base`
`maven-publish`
}
publishing {
publications {
create<MavenPublication>("jitpack") {
groupId = "com.github.Jikoo.OpenInv"
artifactId = "openinvapi"
}
}
}
-49
View File
@@ -1,49 +0,0 @@
<!--
~ Copyright (C) 2011-2022 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>openinvparent</artifactId>
<groupId>com.lishid</groupId>
<version>5.1.1</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>
@@ -16,9 +16,17 @@
package com.lishid.openinv.internal;
import org.bukkit.event.inventory.InventoryType;
import org.jetbrains.annotations.NotNull;
/**
* An {@link ISpecialInventory} representing an ender chest.
*/
public interface ISpecialEnderChest extends ISpecialInventory {
@Override
default @NotNull InventoryType getBukkitType() {
return InventoryType.ENDER_CHEST;
}
}
@@ -18,6 +18,7 @@ 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;
@@ -33,6 +34,13 @@ public interface ISpecialInventory {
*/
@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.
*
@@ -16,35 +16,17 @@
package com.lishid.openinv.internal;
import java.util.List;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.Inventory;
import org.bukkit.event.inventory.InventoryType;
import org.jetbrains.annotations.NotNull;
/**
* An {@link ISpecialInventory} representing a player inventory.
*/
public interface ISpecialPlayerInventory extends ISpecialInventory {
/*
* Player inventory usage varies from all other inventories - as the inventory is technically open at all times,
* if the player is online or has been online while the inventory is open, they are in the viewer list.
*/
@Override
default boolean isInUse() {
Inventory inventory = getBukkitInventory();
List<HumanEntity> viewers = inventory.getViewers();
if (viewers.size() != 1) {
return !viewers.isEmpty();
}
HumanEntity viewer = viewers.get(0);
if (!viewer.getUniqueId().equals(getPlayer().getUniqueId())) {
return true;
}
return viewer.getOpenInventory().getTopInventory().equals(inventory);
default @NotNull InventoryType getBukkitType() {
return InventoryType.PLAYER;
}
}
+27
View File
@@ -0,0 +1,27 @@
plugins {
`java-library`
alias(libs.plugins.paperweight) apply false
alias(libs.plugins.shadow) apply false
}
// Set by Spigot module, used by Paper module to convert to Spigot's version of Mojang mappings.
project.ext.set("craftbukkitPackage", "UNKNOWN")
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
}
// Allow submodules to target higher Java release versions.
// Not currently necessary (as lowest supported version is in the 1.21 range)
// but may become relevant in the future.
java.disableAutoTargetJvm()
// Task to delete ./dist where final files are output.
tasks.register("cleanDist") {
delete("dist")
}
tasks.clean {
// Also delete distribution folder when cleaning.
dependsOn(tasks.named("cleanDist"))
}
+7
View File
@@ -0,0 +1,7 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
}
+60
View File
@@ -0,0 +1,60 @@
import org.gradle.api.file.RegularFile
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.bundling.Jar
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
abstract class ReobfTask: Jar() {
@get:Input
open val spigotVersion: Property<String> = objectFactory.property(String::class.java)
@get:InputFile
open val inputFile: Property<RegularFile> = objectFactory.fileProperty()
@get:Input
open val intermediaryClassifier: Property<String> = objectFactory.property(String::class.java).convention("mojang-mapped")
init {
archiveClassifier.convention("reobf")
}
@TaskAction
override fun copy() {
val spigotVer = spigotVersion.get()
val inFile = inputFile.get().asFile
val obfPath = inFile.resolveSibling(inFile.name.replace(".jar", "-${intermediaryClassifier.get()}.jar"))
// https://www.spigotmc.org/threads/510208/#post-4184317
val specialsource = project.configurations.named("spigotRemap").get().incoming.artifacts.artifacts
.first { it.id.componentIdentifier.toString() == "net.md-5:SpecialSource:1.11.4" }.file.path
val repo = Paths.get(project.repositories.mavenLocal().url)
val spigotDir = repo.resolve("org/spigotmc/spigot/$spigotVer/")
val mappingDir = repo.resolve("org/spigotmc/minecraft-server/$spigotVer/")
// Remap original Mojang-mapped jar to obfuscated intermediary
val mojangServer = spigotDir.resolve("spigot-$spigotVer-remapped-mojang.jar")
val mojangMappings = mappingDir.resolve("minecraft-server-$spigotVer-maps-mojang.txt")
remapPartial(specialsource, mojangServer, mojangMappings, inFile, obfPath, true)
// Remap obfuscated intermediary jar to Spigot and replace original
val obfServer = spigotDir.resolve("spigot-$spigotVer-remapped-obf.jar")
val spigotMappings = mappingDir.resolve("minecraft-server-$spigotVer-maps-spigot.csrg")
remapPartial(specialsource, obfServer, spigotMappings, obfPath, archiveFile.get().asFile, false)
}
private fun remapPartial(specialSource: String, serverJar: Path, mapping: Path, input: File, output: File, reverse: Boolean) {
project.providers.exec {
commandLine("java", "-cp", "$specialSource${File.pathSeparator}$serverJar",
"net.md_5.specialsource.SpecialSource", "--live",
"-i", input.path, "-o", output.path,
"-m", "$mapping",
if (reverse) "--reverse" else "")
}.result.get()
}
}
@@ -0,0 +1,34 @@
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.kotlin.dsl.maven
import org.gradle.kotlin.dsl.repositories
import org.gradle.kotlin.dsl.dependencies
plugins {
`java-library`
}
java {
toolchain.languageVersion = JavaLanguageVersion.of(21)
}
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://hub.spigotmc.org/nexus/content/groups/public/")
}
dependencies {
val libs = versionCatalogs.named("libs")
compileOnly(libs.findLibrary("annotations").orElseThrow())
compileOnly(libs.findLibrary("spigotapi").orElseThrow())
}
tasks {
withType<JavaCompile>().configureEach {
options.release = 21
options.encoding = Charsets.UTF_8.name()
}
withType<Javadoc>().configureEach {
options.encoding = Charsets.UTF_8.name()
}
}
+7
View File
@@ -0,0 +1,7 @@
plugins {
`openinv-base`
}
dependencies {
implementation(project(":openinvapi"))
}
-30
View File
@@ -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.1</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,77 +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.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
public class OpenInventoryView extends InventoryView {
private final @NotNull Player player;
private final @NotNull ISpecialInventory inventory;
private final @NotNull String originalTitle;
private String title;
public OpenInventoryView(
@NotNull Player player,
@NotNull ISpecialInventory inventory,
@NotNull String originalTitle) {
this.player = player;
this.inventory = inventory;
this.originalTitle = originalTitle;
}
@Override
public @NotNull Inventory getTopInventory() {
return inventory.getBukkitInventory();
}
@Override
public @NotNull Inventory getBottomInventory() {
return getPlayer().getInventory();
}
@Override
public @NotNull HumanEntity getPlayer() {
return player;
}
@Override
public @NotNull InventoryType getType() {
return inventory.getBukkitInventory().getType();
}
@Override
public @NotNull String getTitle() {
return title == null ? originalTitle : title;
}
@Override
public @NotNull String getOriginalTitle() {
return originalTitle;
}
@Override
public void setTitle(@NotNull String title) {
this.title = title;
}
}
+9
View File
@@ -0,0 +1,9 @@
# Project meta
group=com.lishid.openinv
version=5.1.9
description=A Bukkit plugin for opening normally-inaccessible inventories.
# Gradle configuration
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
+17
View File
@@ -0,0 +1,17 @@
[versions]
spigotapi = "1.21.4-R0.1-SNAPSHOT"
specialsource = "1.11.4"
planarwrappers = "3.3.0"
annotations = "26.0.2"
paperweight = "2.0.0-beta.14"
shadow = "8.3.6"
[libraries]
spigotapi = { module = "org.spigotmc:spigot-api", version.ref = "spigotapi" }
specialsource = { module = "net.md-5:SpecialSource", version.ref = "specialsource" }
planarwrappers = { module = "com.github.jikoo:planarwrappers", version.ref = "planarwrappers" }
annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" }
[plugins]
paperweight = { id = "io.papermc.paperweight.userdev", version.ref = "paperweight" }
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+234
View File
@@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+89
View File
@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
+60
View File
@@ -0,0 +1,60 @@
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
plugins {
`openinv-base`
alias(libs.plugins.paperweight)
}
//tasks {
// withType<JavaCompile> {
// // OpenPlayer unchecked warning is due to superclass' messy inheritance and legacy methods.
// options.compilerArgs.add("-Xlint:unchecked")
// // PlayerManager uses "deprecated" method matching vanilla to support legacy save data.
// // While vanilla still feels that it is appropriate to use in the load process, we will too.
// options.compilerArgs.add("-Xlint:deprecation")
// }
//}
configurations.all {
resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") {
val paper = candidates.firstOrNull {
it.id.let {
id -> id is ModuleComponentIdentifier && id.module == "paper-api"
}
}
if (paper != null) {
select(paper)
}
because("module is written for Paper servers")
}
}
dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT")
}
val spigot = tasks.register<ShadowJar>("spigotRelocations") {
dependsOn(tasks.jar)
from(sourceSets.main.get().output)
relocate("com.lishid.openinv.internal.common", "com.lishid.openinv.internal.reobf")
relocate("org.bukkit.craftbukkit", "org.bukkit.craftbukkit.${rootProject.extra["craftbukkitPackage"]}")
archiveClassifier = "spigot"
}
configurations {
consumable("spigotRelocated") {
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
}
}
}
artifacts {
add("spigotRelocated", spigot)
}
@@ -1,19 +1,19 @@
package com.lishid.openinv.internal.v1_21_R1;
package com.lishid.openinv.internal.common;
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_R1.container.AnySilentContainer;
import com.lishid.openinv.internal.v1_21_R1.container.OpenEnderChest;
import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory;
import com.lishid.openinv.internal.v1_21_R1.container.Placeholders;
import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager;
import com.lishid.openinv.internal.common.container.AnySilentContainer;
import com.lishid.openinv.internal.common.container.OpenEnderChest;
import com.lishid.openinv.internal.common.container.OpenInventory;
import com.lishid.openinv.internal.common.container.slot.placeholder.PlaceholderLoader;
import com.lishid.openinv.internal.common.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_R1.inventory.CraftInventory;
import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
@@ -24,7 +24,7 @@ import java.util.logging.Logger;
public class InternalAccessor implements Accessor {
private final @NotNull Logger logger;
protected final @NotNull Logger logger;
private final @NotNull PlayerManager manager;
private final @NotNull AnySilentContainer anySilentContainer;
@@ -69,12 +69,11 @@ public class InternalAccessor implements Accessor {
@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);
}
try {
// Reset placeholders to defaults and try to load configuration.
new PlaceholderLoader().load(placeholders);
} catch (Exception e) {
logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e);
}
}
@@ -14,11 +14,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_21_R1.container;
package com.lishid.openinv.internal.common.container;
import com.lishid.openinv.internal.AnySilentContainerBase;
import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenChestMenu;
import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager;
import com.lishid.openinv.internal.common.container.menu.OpenChestMenu;
import com.lishid.openinv.internal.common.player.PlayerManager;
import com.lishid.openinv.util.ReflectionHelper;
import com.lishid.openinv.util.lang.LanguageManager;
import net.minecraft.core.BlockPos;
@@ -61,10 +61,10 @@ public class AnySilentContainer extends AnySilentContainerBase {
this.lang = lang;
try {
try {
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("b");
this.serverPlayerGameModeGameType = ServerPlayerGameMode.class.getDeclaredField("gameModeForPlayer");
this.serverPlayerGameModeGameType.setAccessible(true);
} catch (NoSuchFieldException e) {
logger.warning("ServerPlayerGameMode#gameModeForPlayer's obfuscated name has changed!");
logger.warning("The field ServerPlayerGameMode#gameModeForPlayer is no longer present!");
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.
@@ -1,22 +1,22 @@
package com.lishid.openinv.internal.v1_21_R1.container;
package com.lishid.openinv.internal.common.container;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.InternalOwned;
import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenEnderChestMenu;
import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager;
import com.lishid.openinv.internal.common.container.menu.OpenEnderChestMenu;
import com.lishid.openinv.internal.common.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.StackedContents;
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_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType;
import org.jetbrains.annotations.NotNull;
@@ -90,12 +90,12 @@ public class OpenEnderChest implements Container, StackedContentsCompatible, Int
}
@Override
public ItemStack getItem(int index) {
public @NotNull ItemStack getItem(int index) {
return index >= 0 && index < items.size() ? items.get(index) : ItemStack.EMPTY;
}
@Override
public ItemStack removeItem(int index, int amount) {
public @NotNull ItemStack removeItem(int index, int amount) {
ItemStack itemstack = ContainerHelper.removeItem(items, index, amount);
if (!itemstack.isEmpty()) {
@@ -106,12 +106,12 @@ public class OpenEnderChest implements Container, StackedContentsCompatible, Int
}
@Override
public ItemStack removeItemNoUpdate(int index) {
public @NotNull 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) {
public void setItem(int index, @NotNull ItemStack itemStack) {
if (index >= 0 && index < items.size()) {
items.set(index, itemStack);
}
@@ -128,27 +128,27 @@ public class OpenEnderChest implements Container, StackedContentsCompatible, Int
}
@Override
public boolean stillValid(Player player) {
public boolean stillValid(@NotNull Player player) {
return true;
}
@Override
public List<ItemStack> getContents() {
public @NotNull List<ItemStack> getContents() {
return items;
}
@Override
public void onOpen(CraftHumanEntity craftHumanEntity) {
public void onOpen(@NotNull CraftHumanEntity craftHumanEntity) {
transaction.add(craftHumanEntity);
}
@Override
public void onClose(CraftHumanEntity craftHumanEntity) {
public void onClose(@NotNull CraftHumanEntity craftHumanEntity) {
transaction.remove(craftHumanEntity);
}
@Override
public List<HumanEntity> getViewers() {
public @NotNull List<HumanEntity> getViewers() {
return transaction;
}
@@ -174,7 +174,7 @@ public class OpenEnderChest implements Container, StackedContentsCompatible, Int
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
public void fillStackedContents(@NotNull StackedItemContents stackedContents) {
for (ItemStack itemstack : items) {
stackedContents.accountStack(itemstack);
}
@@ -1,20 +1,21 @@
package com.lishid.openinv.internal.v1_21_R1.container;
package com.lishid.openinv.internal.common.container;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.internal.InternalOwned;
import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenPlayerInventory;
import com.lishid.openinv.internal.v1_21_R1.container.menu.OpenInventoryMenu;
import com.lishid.openinv.internal.v1_21_R1.container.slot.Content;
import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCrafting;
import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCraftingResult;
import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentCursor;
import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentDrop;
import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentEquipment;
import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentList;
import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentOffHand;
import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentViewOnly;
import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly;
import com.lishid.openinv.internal.v1_21_R1.player.PlayerManager;
import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventory;
import com.lishid.openinv.internal.common.container.menu.OpenInventoryMenu;
import com.lishid.openinv.internal.common.container.slot.Content;
import com.lishid.openinv.internal.common.container.slot.ContentCrafting;
import com.lishid.openinv.internal.common.container.slot.ContentCraftingResult;
import com.lishid.openinv.internal.common.container.slot.ContentCursor;
import com.lishid.openinv.internal.common.container.slot.ContentDrop;
import com.lishid.openinv.internal.common.container.slot.ContentEquipment;
import com.lishid.openinv.internal.common.container.slot.ContentList;
import com.lishid.openinv.internal.common.container.slot.ContentOffHand;
import com.lishid.openinv.internal.common.container.slot.ContentViewOnly;
import com.lishid.openinv.internal.common.container.slot.SlotViewOnly;
import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.common.player.PlayerManager;
import net.minecraft.ChatFormatting;
import net.minecraft.core.NonNullList;
import net.minecraft.network.chat.Component;
@@ -29,8 +30,8 @@ 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_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType;
import org.jetbrains.annotations.NotNull;
@@ -191,12 +192,16 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
};
}
});
slots.set(startIndex + 11, new ContentCraftingResult(owner));
slots.set(startIndex + 11, getCraftingResult(owner));
}
return startIndex + listSize;
}
protected Content getCraftingResult(@NotNull ServerPlayer serverPlayer) {
return new ContentCraftingResult(serverPlayer);
}
public Slot getMenuSlot(int index, int x, int y) {
return slots.get(index).asSlot(this, index, x, y);
}
@@ -308,12 +313,12 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
public void setChanged() {}
@Override
public boolean stillValid(Player player) {
public boolean stillValid(@NotNull Player player) {
return true;
}
@Override
public List<ItemStack> getContents() {
public @NotNull List<ItemStack> getContents() {
NonNullList<ItemStack> contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY);
for (int i = 0; i < getContainerSize(); ++i) {
contents.set(i, getItem(i));
@@ -337,7 +342,7 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
}
@Override
public org.bukkit.entity.Player getOwner() {
public @NotNull org.bukkit.entity.Player getOwner() {
return owner.getBukkitEntity();
}
@@ -1,9 +1,10 @@
package com.lishid.openinv.internal.v1_21_R1.container.bukkit;
package com.lishid.openinv.internal.common.container.bukkit;
import com.lishid.openinv.internal.ViewOnly;
import net.minecraft.world.Container;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftInventory;
import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -17,8 +18,16 @@ import java.util.ListIterator;
*/
public class OpenDummyInventory extends CraftInventory implements ViewOnly {
public OpenDummyInventory(Container inventory) {
private final InventoryType type;
public OpenDummyInventory(Container inventory, InventoryType type) {
super(inventory);
this.type = type;
}
@Override
public @NotNull InventoryType getType() {
return type;
}
@Override
@@ -53,7 +62,7 @@ public class OpenDummyInventory extends CraftInventory implements ViewOnly {
}
@Override
public ItemStack[] getContents() {
public ItemStack @NotNull [] getContents() {
return new ItemStack[getSize()];
}
@@ -63,7 +72,7 @@ public class OpenDummyInventory extends CraftInventory implements ViewOnly {
}
@Override
public @NotNull ItemStack[] getStorageContents() {
public @NotNull ItemStack @NotNull [] getStorageContents() {
return new ItemStack[getSize()];
}
@@ -0,0 +1,136 @@
package com.lishid.openinv.internal.common.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) {
}
@Deprecated
@Override
public @NotNull ItemStack getItemInHand() {
return new ItemStack(Material.AIR);
}
@Deprecated
@Override
public void setItemInHand(@Nullable ItemStack stack) {
}
@Override
public int getHeldItemSlot() {
return 0;
}
@Override
public void setHeldItemSlot(int slot) {
}
}
@@ -1,12 +1,12 @@
package com.lishid.openinv.internal.v1_21_R1.container.bukkit;
package com.lishid.openinv.internal.common.container.bukkit;
import com.google.common.base.Preconditions;
import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory;
import com.lishid.openinv.internal.common.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_R1.inventory.CraftInventory;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
@@ -170,11 +170,13 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInvento
getInventory().getOwnerHandle().getInventory().offhand.set(0, CraftItemStack.asNMSCopy(item));
}
@Deprecated
@Override
public @NotNull ItemStack getItemInHand() {
return getItemInMainHand();
}
@Deprecated
@Override
public void setItemInHand(@Nullable ItemStack stack) {
setItemInMainHand(stack);
@@ -1,6 +1,6 @@
package com.lishid.openinv.internal.v1_21_R1.container.bukkit;
package com.lishid.openinv.internal.common.container.bukkit;
import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory;
import com.lishid.openinv.internal.common.container.OpenInventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -1,11 +1,11 @@
package com.lishid.openinv.internal.v1_21_R1.container.menu;
package com.lishid.openinv.internal.common.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_R1.container.bukkit.OpenDummyInventory;
import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotPlaceholder;
import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly;
import com.lishid.openinv.internal.common.container.bukkit.OpenDummyInventory;
import com.lishid.openinv.internal.common.container.slot.SlotPlaceholder;
import com.lishid.openinv.internal.common.container.slot.SlotViewOnly;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import net.minecraft.server.level.ServerPlayer;
@@ -21,7 +21,7 @@ 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_R1.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
@@ -45,7 +45,7 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
protected final boolean viewOnly;
protected final boolean ownContainer;
protected final int topSize;
private CraftInventoryView<OpenChestMenu<T>> bukkitEntity;
private CraftInventoryView<OpenChestMenu<T>, Inventory> bukkitEntity;
// Syncher fields
private @Nullable ContainerSynchronizer synchronizer;
private final List<DataSlot> dataSlots = new ArrayList<>();
@@ -124,15 +124,16 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
protected void preSlotSetup() {}
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
Slot slot = new Slot(container, index, x, y);
if (viewOnly) {
return new SlotViewOnly(container, index, x, y);
return SlotViewOnly.wrap(slot);
}
return new Slot(container, index, x, y);
return slot;
}
@Override
public final @NotNull CraftInventoryView<OpenChestMenu<T>> getBukkitView() {
public final @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> getBukkitView() {
if (bukkitEntity == null) {
bukkitEntity = createBukkitEntity();
}
@@ -140,10 +141,10 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
return bukkitEntity;
}
protected @NotNull CraftInventoryView<OpenChestMenu<T>> createBukkitEntity() {
protected @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> createBukkitEntity() {
Inventory top;
if (viewOnly) {
top = new OpenDummyInventory(container);
top = new OpenDummyInventory(container, container.getBukkitType());
} else {
top = container.getBukkitInventory();
}
@@ -176,9 +177,7 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
private int getTopSize(ServerPlayer viewer) {
MenuType<?> menuType = getType();
if (menuType == null) {
throw new IllegalStateException("MenuType cannot be null!");
} else if (menuType == MenuType.GENERIC_9x1) {
if (menuType == MenuType.GENERIC_9x1) {
return 9;
} else if (menuType == MenuType.GENERIC_9x2) {
return 18;
@@ -284,7 +283,7 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
}
@Override
public void clicked(int i, int j, ClickType clickType, Player player) {
public void clicked(int i, int j, @NotNull ClickType clickType, @NotNull Player player) {
if (viewOnly) {
if (clickType == ClickType.QUICK_CRAFT) {
sendAllDataToRemote();
@@ -295,13 +294,13 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
}
@Override
public boolean stillValid(Player player) {
public boolean stillValid(@NotNull 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) {
protected @NotNull Slot addSlot(@NotNull Slot slot) {
slot.index = this.slots.size();
this.slots.add(slot);
this.lastSlots.add(ItemStack.EMPTY);
@@ -310,7 +309,7 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
}
@Override
protected DataSlot addDataSlot(DataSlot dataSlot) {
protected @NotNull DataSlot addDataSlot(@NotNull DataSlot dataSlot) {
this.dataSlots.add(dataSlot);
this.remoteDataSlots.add(0);
return dataSlot;
@@ -324,7 +323,7 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
}
@Override
public void addSlotListener(ContainerListener containerListener) {
public void addSlotListener(@NotNull ContainerListener containerListener) {
if (!this.containerListeners.contains(containerListener)) {
this.containerListeners.add(containerListener);
this.broadcastChanges();
@@ -332,7 +331,7 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
}
@Override
public void setSynchronizer(ContainerSynchronizer containerSynchronizer) {
public void setSynchronizer(@NotNull ContainerSynchronizer containerSynchronizer) {
this.synchronizer = containerSynchronizer;
this.sendAllDataToRemote();
}
@@ -364,7 +363,7 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
}
@Override
public void removeSlotListener(ContainerListener containerListener) {
public void removeSlotListener(@NotNull ContainerListener containerListener) {
this.containerListeners.remove(containerListener);
}
@@ -1,6 +1,6 @@
package com.lishid.openinv.internal.v1_21_R1.container.menu;
package com.lishid.openinv.internal.common.container.menu;
import com.lishid.openinv.internal.v1_21_R1.container.OpenEnderChest;
import com.lishid.openinv.internal.common.container.OpenEnderChest;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
@@ -18,7 +18,7 @@ public class OpenEnderChestMenu extends OpenChestMenu<OpenEnderChest> {
}
@Override
public ItemStack quickMoveStack(Player player, int index) {
public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) {
if (viewOnly) {
return ItemStack.EMPTY;
}
@@ -1,12 +1,12 @@
package com.lishid.openinv.internal.v1_21_R1.container.menu;
package com.lishid.openinv.internal.common.container.menu;
import com.google.common.base.Preconditions;
import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory;
import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenDummyInventory;
import com.lishid.openinv.internal.v1_21_R1.container.bukkit.OpenPlayerInventorySelf;
import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentDrop;
import com.lishid.openinv.internal.v1_21_R1.container.slot.ContentEquipment;
import com.lishid.openinv.internal.v1_21_R1.container.slot.SlotViewOnly;
import com.lishid.openinv.internal.common.container.OpenInventory;
import com.lishid.openinv.internal.common.container.bukkit.OpenDummyPlayerInventory;
import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventorySelf;
import com.lishid.openinv.internal.common.container.slot.ContentDrop;
import com.lishid.openinv.internal.common.container.slot.ContentEquipment;
import com.lishid.openinv.internal.common.container.slot.SlotViewOnly;
import com.lishid.openinv.util.Permissions;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.EquipmentSlot;
@@ -15,8 +15,8 @@ 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_R1.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.v1_21_R1.inventory.CraftItemStack;
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
@@ -99,10 +99,10 @@ public class OpenInventoryMenu extends OpenChestMenu<OpenInventory> {
}
@Override
protected @NotNull CraftInventoryView<OpenChestMenu<OpenInventory>> createBukkitEntity() {
protected @NotNull CraftInventoryView<OpenChestMenu<OpenInventory>, Inventory> createBukkitEntity() {
org.bukkit.inventory.Inventory bukkitInventory;
if (viewOnly) {
bukkitInventory = new OpenDummyInventory(container);
bukkitInventory = new OpenDummyPlayerInventory(container);
} else if (ownContainer) {
bukkitInventory = new OpenPlayerInventorySelf(container, offset);
} else {
@@ -195,7 +195,7 @@ public class OpenInventoryMenu extends OpenChestMenu<OpenInventory> {
}
@Override
public ItemStack quickMoveStack(Player player, int index) {
public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) {
if (viewOnly) {
return ItemStack.EMPTY;
}
@@ -1,4 +1,4 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
@@ -1,6 +1,7 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import com.lishid.openinv.internal.v1_21_R1.container.Placeholders;
import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.common.player.OpenPlayer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
@@ -30,11 +31,11 @@ public class ContentCrafting implements Content {
return isAvailable(holder);
}
static boolean isAvailable(@NotNull ServerPlayer holder) {
public 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();
return OpenPlayer.isConnected(holder.connection) && holder.gameMode.isSurvival();
}
@Override
@@ -107,12 +108,12 @@ public class ContentCrafting implements Content {
}
@Override
public boolean mayPickup(Player player) {
public boolean mayPickup(@NotNull Player player) {
return isAvailable();
}
@Override
public boolean mayPlace(ItemStack itemStack) {
public boolean mayPlace(@NotNull ItemStack itemStack) {
return isAvailable();
}
@@ -1,6 +1,6 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import com.lishid.openinv.internal.v1_21_R1.container.Placeholders;
import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.inventory.InventoryMenu;
@@ -23,7 +23,7 @@ public class ContentCraftingResult extends ContentViewOnly {
@Override
public ItemStack get() {
InventoryMenu inventoryMenu = holder.inventoryMenu;
return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).getItem();
return inventoryMenu.getResultSlot().getItem();
}
@Override
@@ -35,7 +35,7 @@ public class ContentCraftingResult extends ContentViewOnly {
return Placeholders.survivalOnly(holder);
}
InventoryMenu inventoryMenu = holder.inventoryMenu;
return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).getItem();
return inventoryMenu.getResultSlot().getItem();
}
};
}
@@ -1,6 +1,7 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import com.lishid.openinv.internal.v1_21_R1.container.Placeholders;
import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.common.player.OpenPlayer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
@@ -63,7 +64,7 @@ public class ContentCursor implements Content {
// 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();
return OpenPlayer.isConnected(holder.connection) && holder.gameMode.isSurvival();
}
@Override
@@ -93,12 +94,12 @@ public class ContentCursor implements Content {
}
@Override
public boolean mayPickup(Player player) {
public boolean mayPickup(@NotNull Player player) {
return isAvailable();
}
@Override
public boolean mayPlace(ItemStack itemStack) {
public boolean mayPlace(@NotNull ItemStack itemStack) {
return isAvailable();
}
@@ -1,6 +1,7 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import com.lishid.openinv.internal.v1_21_R1.container.Placeholders;
import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.common.player.OpenPlayer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.inventory.Slot;
@@ -63,14 +64,14 @@ public class ContentDrop implements Content {
@Override
public ItemStack getOrDefault() {
return holder.connection != null && !holder.connection.isDisconnected()
return OpenPlayer.isConnected(holder.connection)
? Placeholders.drop
: Placeholders.blockedOffline;
}
@Override
public boolean mayPlace(ItemStack itemStack) {
return holder.connection != null && !holder.connection.isDisconnected();
public boolean mayPlace(@NotNull ItemStack itemStack) {
return OpenPlayer.isConnected(holder.connection);
}
@Override
@@ -1,6 +1,6 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import com.lishid.openinv.internal.v1_21_R1.container.Placeholders;
import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
@@ -65,12 +65,12 @@ public class ContentEquipment extends ContentList {
}
@Override
public boolean mayPlace(ItemStack var0) {
public boolean mayPlace(@NotNull ItemStack itemStack) {
if (viewer == null) {
return true;
}
return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(var0) == equipmentSlot;
return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(itemStack) == equipmentSlot;
}
}
@@ -1,4 +1,4 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
@@ -1,8 +1,11 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import com.lishid.openinv.internal.common.player.OpenPlayer;
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;
@@ -34,10 +37,13 @@ public class ContentOffHand extends ContentEquipment {
return new SlotEquipment(container, slot, x, y) {
@Override
public void setChanged() {
if (holder.connection != null
&& !holder.connection.isDisconnected()
&& holder.containerMenu != holder.inventoryMenu) {
holder.resendItemInHands();
if (OpenPlayer.isConnected(holder.connection) && holder.containerMenu != holder.inventoryMenu) {
holder.connection.send(
new ClientboundContainerSetSlotPacket(
holder.inventoryMenu.containerId,
holder.inventoryMenu.incrementStateId(),
InventoryMenu.SHIELD_SLOT,
holder.getOffhandItem()));
}
}
};
@@ -1,4 +1,4 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
@@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
*/
public class ContentViewOnly implements Content {
@NotNull ServerPlayer holder;
protected @NotNull ServerPlayer holder;
public ContentViewOnly(@NotNull ServerPlayer holder) {
this.holder = holder;
@@ -1,4 +1,4 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import net.minecraft.world.Container;
import net.minecraft.world.inventory.Slot;
@@ -1,6 +1,6 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.common.container.slot;
import com.lishid.openinv.internal.v1_21_R1.container.Placeholders;
import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
@@ -45,20 +45,20 @@ public class SlotViewOnly extends SlotPlaceholder {
}
@Override
public void onQuickCraft(ItemStack var0, ItemStack var1) {
public void onQuickCraft(@NotNull ItemStack itemStack1, @NotNull ItemStack itemStack2) {
}
@Override
public void onTake(Player var0, ItemStack var1) {
public void onTake(@NotNull Player player, @NotNull ItemStack itemStack) {
}
@Override
public boolean mayPlace(ItemStack var0) {
public boolean mayPlace(@NotNull ItemStack itemStack) {
return false;
}
@Override
public ItemStack getItem() {
public @NotNull ItemStack getItem() {
return ItemStack.EMPTY;
}
@@ -68,15 +68,15 @@ public class SlotViewOnly extends SlotPlaceholder {
}
@Override
public void setByPlayer(ItemStack newStack) {
public void setByPlayer(@NotNull ItemStack newStack) {
}
@Override
public void setByPlayer(ItemStack newStack, ItemStack oldStack) {
public void setByPlayer(@NotNull ItemStack newStack, @NotNull ItemStack oldStack) {
}
@Override
public void set(ItemStack var0) {
public void set(@NotNull ItemStack itemStack) {
}
@Override
@@ -89,17 +89,17 @@ public class SlotViewOnly extends SlotPlaceholder {
}
@Override
public int getMaxStackSize(ItemStack itemStack) {
public int getMaxStackSize(@NotNull ItemStack itemStack) {
return 0;
}
@Override
public ItemStack remove(int amount) {
public @NotNull ItemStack remove(int amount) {
return ItemStack.EMPTY;
}
@Override
public boolean mayPickup(Player var0) {
public boolean mayPickup(@NotNull Player player) {
return false;
}
@@ -109,27 +109,27 @@ public class SlotViewOnly extends SlotPlaceholder {
}
@Override
public Optional<ItemStack> tryRemove(int var0, int var1, Player var2) {
public @NotNull Optional<ItemStack> tryRemove(int var0, int var1, @NotNull Player player) {
return Optional.empty();
}
@Override
public ItemStack safeTake(int var0, int var1, Player var2) {
public @NotNull ItemStack safeTake(int var0, int var1, @NotNull Player player) {
return ItemStack.EMPTY;
}
@Override
public ItemStack safeInsert(ItemStack itemStack) {
public @NotNull ItemStack safeInsert(@NotNull ItemStack itemStack) {
return itemStack;
}
@Override
public ItemStack safeInsert(ItemStack itemStack, int amount) {
public @NotNull ItemStack safeInsert(@NotNull ItemStack itemStack, int amount) {
return itemStack;
}
@Override
public boolean allowModification(Player var0) {
public boolean allowModification(@NotNull Player player) {
return false;
}
@@ -0,0 +1,18 @@
package com.lishid.openinv.internal.common.container.slot.placeholder;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomModelData;
import java.util.List;
public class PlaceholderLoader extends PlaceholderLoaderBase {
private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(List.of(), List.of(), List.of("openinv:custom"), List.of());
@Override
protected void addModelData(ItemStack itemStack) {
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
}
}
@@ -1,4 +1,4 @@
package com.lishid.openinv.internal.v1_21_R1.container;
package com.lishid.openinv.internal.common.container.slot.placeholder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
@@ -7,12 +7,10 @@ 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;
@@ -20,58 +18,59 @@ 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_R1.CraftRegistry;
import org.bukkit.craftbukkit.CraftRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.EnumMap;
import java.util.List;
import java.util.Optional;
public final class Placeholders {
public abstract class PlaceholderLoaderBase {
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 {
protected PlaceholderLoaderBase() {
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);
Placeholders.BLOCKED_GAME_TYPE.put(type, typeItem);
}
Placeholders.craftingOutput = defaultCraftingOutput();
Placeholders.cursor = defaultCursor();
Placeholders.drop = defaultDrop();
Placeholders.emptyHelmet = getEmptyArmor(Items.LEATHER_HELMET);
Placeholders.emptyChestplate = getEmptyArmor(Items.LEATHER_CHESTPLATE);
Placeholders.emptyLeggings = getEmptyArmor(Items.LEATHER_LEGGINGS);
Placeholders.emptyBoots = getEmptyArmor(Items.LEATHER_BOOTS);
Placeholders.emptyOffHand = defaultShield();
Placeholders.notSlot = defaultNotSlot();
Placeholders.blockedOffline = defaultBlockedOffline();
}
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)));
public void load(@Nullable ConfigurationSection section) throws Exception {
Placeholders.craftingOutput = parse(section, "crafting-output", Placeholders.craftingOutput);
Placeholders.cursor = parse(section, "cursor", Placeholders.cursor);
Placeholders.drop = parse(section, "drop", Placeholders.drop);
Placeholders.emptyHelmet = parse(section, "empty-helmet", Placeholders.emptyHelmet);
Placeholders.emptyChestplate = parse(section, "empty-chestplate", Placeholders.emptyChestplate);
Placeholders.emptyLeggings = parse(section, "empty-leggings", Placeholders.emptyLeggings);
Placeholders.emptyBoots = parse(section, "empty-boots", Placeholders.emptyBoots);
Placeholders.emptyOffHand = parse(section, "empty-off-hand", Placeholders.emptyOffHand);
Placeholders.notSlot = parse(section, "not-a-slot", Placeholders.notSlot);
Placeholders.blockedOffline = parse(section, "blocked.offline", Placeholders.blockedOffline);
Placeholders.BLOCKED_GAME_TYPE.put(GameType.CREATIVE, parse(section, "blocked.creative", Placeholders.BLOCKED_GAME_TYPE.get(GameType.CREATIVE)));
Placeholders.BLOCKED_GAME_TYPE.put(GameType.SPECTATOR, parse(section, "blocked.spectator", Placeholders.BLOCKED_GAME_TYPE.get(GameType.SPECTATOR)));
}
private static @NotNull ItemStack parse(
@NotNull ConfigurationSection section,
@Nullable ConfigurationSection section,
@NotNull String path,
@NotNull ItemStack defaultStack) throws Exception {
if (section == null) {
return defaultStack;
}
String itemText = section.getString(path);
if (itemText == null) {
@@ -83,69 +82,64 @@ public final class Placeholders {
return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack);
}
public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) {
if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) {
return blockedOffline;
}
protected abstract void addModelData(ItemStack itemStack);
return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY);
}
private static ItemStack defaultCraftingOutput() {
protected @NotNull 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);
addModelData(itemStack);
return itemStack;
}
private static ItemStack defaultCursor() {
protected @NotNull ItemStack defaultCursor() {
// Cursor-like banner with no tooltip
ItemStack itemStack = new ItemStack(Items.WHITE_BANNER);
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
Registry<BannerPattern> bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN);
BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT);
BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT);
BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER);
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);
addModelData(itemStack);
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
return itemStack;
}
private static ItemStack defaultDrop() {
protected @NotNull 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);
addModelData(itemStack);
return itemStack;
}
private static ItemStack getEmptyArmor(ItemLike item) {
protected @NotNull ItemStack getEmptyArmor(@NotNull 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);
addModelData(itemStack);
return itemStack;
}
private static ItemStack getEmptyShield() {
protected @NotNull ItemStack defaultShield() {
// Shield with "missing texture" pattern, magenta and black squares.
ItemStack itemStack = new ItemStack(Items.SHIELD);
itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA);
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
Registry<BannerPattern> bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN);
BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL);
BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT);
BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT);
BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT);
BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT);
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),
@@ -154,19 +148,19 @@ public final class Placeholders {
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);
addModelData(itemStack);
return itemStack;
}
private static ItemStack defaultNotSlot() {
protected @NotNull 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);
addModelData(itemStack);
return itemStack;
}
private static ItemStack defaultBlockedOffline() {
protected @NotNull ItemStack defaultBlockedOffline() {
// Barrier: "Not available - Offline"
ItemStack itemStack = new ItemStack(Items.BARRIER);
itemStack.set(DataComponents.ITEM_NAME,
@@ -176,8 +170,4 @@ public final class Placeholders {
return itemStack;
}
private Placeholders() {
throw new IllegalStateException("Cannot create instance of utility class.");
}
}
@@ -0,0 +1,37 @@
package com.lishid.openinv.internal.common.container.slot.placeholder;
import com.lishid.openinv.internal.common.player.OpenPlayer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameType;
import org.jetbrains.annotations.NotNull;
import java.util.EnumMap;
public final class Placeholders {
static final @NotNull EnumMap<GameType, ItemStack> BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class);
public static @NotNull ItemStack craftingOutput = ItemStack.EMPTY;
public static @NotNull ItemStack cursor = ItemStack.EMPTY;
public static @NotNull ItemStack drop = ItemStack.EMPTY;
public static @NotNull ItemStack emptyHelmet = ItemStack.EMPTY;
public static @NotNull ItemStack emptyChestplate = ItemStack.EMPTY;
public static @NotNull ItemStack emptyLeggings = ItemStack.EMPTY;
public static @NotNull ItemStack emptyBoots = ItemStack.EMPTY;
public static @NotNull ItemStack emptyOffHand = ItemStack.EMPTY;
public static @NotNull ItemStack notSlot = ItemStack.EMPTY;
public static @NotNull ItemStack blockedOffline = ItemStack.EMPTY;
public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) {
if (!OpenPlayer.isConnected(serverPlayer.connection)) {
return blockedOffline;
}
return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY);
}
private Placeholders() {
throw new IllegalStateException("Cannot create instance of utility class.");
}
}
@@ -1,4 +1,4 @@
package com.lishid.openinv.internal.v1_21_R1.player;
package com.lishid.openinv.internal.common.player;
import com.lishid.openinv.event.OpenEvents;
import com.mojang.logging.LogUtils;
@@ -8,9 +8,10 @@ import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NumericTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.storage.PlayerDataStorage;
import org.bukkit.craftbukkit.v1_21_R1.CraftServer;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -51,6 +52,7 @@ public class OpenPlayer extends CraftPlayer {
"SpawnAngle",
"SpawnDimension",
"raid_omen_position",
"ender_pearls",
// Player#addAdditionalSaveData(CompoundTag)
"ShoulderEntityLeft",
"ShoulderEntityRight",
@@ -174,4 +176,8 @@ public class OpenPlayer extends CraftPlayer {
}
}
public static boolean isConnected(@Nullable ServerGamePacketListenerImpl connection) {
return connection != null && !connection.isDisconnected();
}
}
@@ -1,16 +1,18 @@
package com.lishid.openinv.internal.v1_21_R1.player;
package com.lishid.openinv.internal.common.player;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.v1_21_R1.container.OpenEnderChest;
import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory;
import com.lishid.openinv.internal.common.container.OpenEnderChest;
import com.lishid.openinv.internal.common.container.OpenInventory;
import com.mojang.authlib.GameProfile;
import com.mojang.serialization.Dynamic;
import io.papermc.paper.adventure.PaperAdventure;
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;
@@ -22,10 +24,10 @@ import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_21_R1.CraftServer;
import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_R1.event.CraftEventFactory;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
@@ -37,18 +39,7 @@ 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;
protected final @NotNull Logger logger;
private @Nullable Field bukkitEntity;
public PlayerManager(@NotNull Logger logger) {
@@ -106,7 +97,7 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
return null;
}
private @NotNull ServerPlayer createNewPlayer(
protected @NotNull ServerPlayer createNewPlayer(
@NotNull MinecraftServer server,
@NotNull ServerLevel worldServer,
@NotNull final OfflinePlayer offline) {
@@ -123,7 +114,8 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
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).
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);
@@ -156,10 +148,8 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
// Game type settings are also loaded separately.
player.loadGameTypes(loadedData);
if (paper) {
// Paper: world is not loaded by ServerPlayer#load(CompoundTag).
parseWorld(player, loadedData);
}
// World is not loaded by ServerPlayer#load(CompoundTag) on Paper.
parseWorld(player, loadedData);
return true;
}
@@ -179,17 +169,26 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
.resultOrPartial(logger::warning)
.map(player.server::getLevel)
// If ServerLevel exists, set, otherwise move to spawn.
.ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null));
.ifPresentOrElse(player::setServerLevel, () -> spawnInDefaultWorld(player));
return;
}
if (bukkitWorld == null) {
player.spawnIn(null);
spawnInDefaultWorld(player);
return;
}
player.setServerLevel(((CraftWorld) bukkitWorld).getHandle());
}
private void injectPlayer(ServerPlayer player) throws IllegalAccessException {
private void spawnInDefaultWorld(ServerPlayer player) {
ServerLevel level = player.server.getLevel(Level.OVERWORLD);
if (level != null) {
player.spawnIn(level);
} else {
logger.warning("Tried to load player with invalid world when no fallback was available!");
}
}
protected void injectPlayer(ServerPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
@@ -221,7 +220,7 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
public @Nullable InventoryView openInventory(@NotNull Player bukkitPlayer, @NotNull ISpecialInventory inventory, boolean viewOnly) {
ServerPlayer player = getHandle(bukkitPlayer);
if (player.connection == null) {
if (!OpenPlayer.isConnected(player.connection)) {
return null;
}
@@ -248,15 +247,21 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
// resulting in a title change but no other state modifications (like cursor position).
menu.setTitle(title);
menu = CraftEventFactory.callInventoryOpenEvent(player, menu, false);
var pair = CraftEventFactory.callInventoryOpenEventWithTitle(player, menu);
menu = pair.getSecond();
// Menu is null if event is cancelled.
if (menu == null) {
return null;
}
var newTitle = pair.getFirst();
if (newTitle != null) {
title = PaperAdventure.asVanilla(newTitle);
}
player.containerMenu = menu;
player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), menu.getTitle()));
player.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), title));
player.initMenu(menu);
return menu.getBukkitView();
+27
View File
@@ -0,0 +1,27 @@
plugins {
`openinv-base`
alias(libs.plugins.paperweight)
}
configurations.all {
resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") {
val paper = candidates.firstOrNull {
it.id.let {
id -> id is ModuleComponentIdentifier && id.module == "paper-api"
}
}
if (paper != null) {
select(paper)
}
because("module is written for Paper servers")
}
}
dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
implementation(project(":openinvadaptercommon"))
implementation(project(":openinvadapterpaper1_21_3"))
paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT")
}
@@ -1,29 +1,37 @@
package com.lishid.openinv.internal.v1_20_R3;
package com.lishid.openinv.internal.paper1_21_1;
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.common.container.AnySilentContainer;
import com.lishid.openinv.internal.common.container.OpenEnderChest;
import com.lishid.openinv.internal.paper1_21_1.container.OpenInventory;
import com.lishid.openinv.internal.paper1_21_1.container.slot.placeholder.PlaceholderLoader;
import com.lishid.openinv.internal.paper1_21_1.player.PlayerManager;
import com.lishid.openinv.util.lang.LanguageManager;
import net.minecraft.world.Container;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.craftbukkit.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) {
manager = new PlayerManager(logger, lang);
anySilentContainer = new AnySilentContainer(logger, lang);
this.logger = logger;
this.manager = new PlayerManager(logger);
this.anySilentContainer = new AnySilentContainer(logger, lang);
}
@Override
@@ -38,12 +46,12 @@ public class InternalAccessor implements Accessor {
@Override
public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) {
return new SpecialPlayerInventory(player, player.isOnline());
return new OpenInventory(player);
}
@Override
public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) {
return new SpecialEnderChest(player, player.isOnline());
return new OpenEnderChest(player);
}
@Override
@@ -59,6 +67,14 @@ public class InternalAccessor implements Accessor {
}
@Override
public void reload(@NotNull ConfigurationSection config) {}
public void reload(@NotNull ConfigurationSection config) {
ConfigurationSection placeholders = config.getConfigurationSection("placeholders");
try {
// Reset placeholders to defaults and try to load configuration.
new PlaceholderLoader().load(placeholders);
} catch (Exception e) {
logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e);
}
}
}
@@ -0,0 +1,20 @@
package com.lishid.openinv.internal.paper1_21_1.container;
import com.lishid.openinv.internal.common.container.slot.Content;
import com.lishid.openinv.internal.paper1_21_1.container.slot.ContentCraftingResult;
import net.minecraft.server.level.ServerPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class OpenInventory extends com.lishid.openinv.internal.common.container.OpenInventory {
public OpenInventory(@NotNull Player bukkitPlayer) {
super(bukkitPlayer);
}
@Override
protected Content getCraftingResult(@NotNull ServerPlayer serverPlayer) {
return new ContentCraftingResult(serverPlayer);
}
}
@@ -0,0 +1,46 @@
package com.lishid.openinv.internal.paper1_21_1.container.slot;
import com.lishid.openinv.internal.common.container.slot.ContentCrafting;
import com.lishid.openinv.internal.common.container.slot.ContentViewOnly;
import com.lishid.openinv.internal.common.container.slot.SlotViewOnly;
import com.lishid.openinv.internal.common.container.slot.placeholder.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;
public class ContentCraftingResult extends ContentViewOnly {
public ContentCraftingResult(@NotNull ServerPlayer holder) {
super(holder);
}
@Override
public ItemStack get() {
InventoryMenu inventoryMenu = holder.inventoryMenu;
return inventoryMenu.getSlot(inventoryMenu.getResultSlotIndex()).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.getSlot(inventoryMenu.getResultSlotIndex()).getItem();
}
};
}
@Override
public InventoryType.SlotType getSlotType() {
return InventoryType.SlotType.RESULT;
}
}
@@ -0,0 +1,65 @@
package com.lishid.openinv.internal.paper1_21_1.container.slot.placeholder;
import com.lishid.openinv.internal.paper1_21_3.container.slot.placeholder.NumericDataPlaceholderLoader;
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.util.Unit;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
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.craftbukkit.CraftRegistry;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class PlaceholderLoader extends NumericDataPlaceholderLoader {
@Override
protected @NotNull ItemStack defaultCursor() {
// Cursor-like banner with no tooltip
ItemStack itemStack = new ItemStack(Items.WHITE_BANNER);
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
Registry<BannerPattern> bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN);
BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT);
BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT);
BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER);
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))));
addModelData(itemStack);
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
return itemStack;
}
@Override
protected @NotNull ItemStack defaultShield() {
// Shield with "missing texture" pattern, magenta and black squares.
ItemStack itemStack = new ItemStack(Items.SHIELD);
itemStack.set(DataComponents.BASE_COLOR, DyeColor.MAGENTA);
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
Registry<BannerPattern> bannerPatterns = minecraftRegistry.registryOrThrow(Registries.BANNER_PATTERN);
BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL);
BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT);
BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT);
BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT);
BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT);
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);
addModelData(itemStack);
return itemStack;
}
}
@@ -0,0 +1,54 @@
package com.lishid.openinv.internal.paper1_21_1.player;
import com.mojang.authlib.GameProfile;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.ChatVisiblity;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.util.logging.Logger;
public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager {
public PlayerManager(@NotNull Logger logger) {
super(logger);
}
@Override
protected @NotNull ServerPlayer createNewPlayer(
@NotNull MinecraftServer server,
@NotNull ServerLevel worldServer,
@NotNull final OfflinePlayer offline) {
// See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile)
// See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket)
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
ClientInformation dummyInfo = new ClientInformation(
"en_us",
1, // Reduce distance just in case.
ChatVisiblity.HIDDEN, // Don't accept chat.
false,
ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION,
ServerPlayer.DEFAULT_MAIN_HAND,
true,
false // Don't list in player list (not that this player is in the list anyway).
);
ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
logger.log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
}
return entity;
}
}
+26
View File
@@ -0,0 +1,26 @@
plugins {
`openinv-base`
alias(libs.plugins.paperweight)
}
configurations.all {
resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") {
val paper = candidates.firstOrNull {
it.id.let {
id -> id is ModuleComponentIdentifier && id.module == "paper-api"
}
}
if (paper != null) {
select(paper)
}
because("module is written for Paper servers")
}
}
dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
implementation(project(":openinvadaptercommon"))
paperweight.paperDevBundle("1.21.3-R0.1-SNAPSHOT")
}
@@ -0,0 +1,28 @@
package com.lishid.openinv.internal.paper1_21_3;
import com.lishid.openinv.internal.paper1_21_3.container.slot.placeholder.NumericDataPlaceholderLoader;
import com.lishid.openinv.util.lang.LanguageManager;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import java.util.logging.Level;
import java.util.logging.Logger;
public class InternalAccessor extends com.lishid.openinv.internal.common.InternalAccessor {
public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
super(logger, lang);
}
@Override
public void reload(@NotNull ConfigurationSection config) {
ConfigurationSection placeholders = config.getConfigurationSection("placeholders");
try {
// Reset placeholders to defaults and try to load configuration.
new NumericDataPlaceholderLoader().load(placeholders);
} catch (Exception e) {
logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e);
}
}
}
@@ -0,0 +1,31 @@
package com.lishid.openinv.internal.paper1_21_3.container.slot.placeholder;
import com.lishid.openinv.internal.common.container.slot.placeholder.PlaceholderLoaderBase;
import net.minecraft.core.HolderLookup;
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.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.level.block.entity.BannerPattern;
import net.minecraft.world.level.block.entity.BannerPatternLayers;
import net.minecraft.world.level.block.entity.BannerPatterns;
import org.bukkit.craftbukkit.CraftRegistry;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class NumericDataPlaceholderLoader extends PlaceholderLoaderBase {
private static final CustomModelData DEFAULT_CUSTOM_MODEL_DATA = new CustomModelData(9999);
@Override
protected void addModelData(ItemStack itemStack) {
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
}
}
+66
View File
@@ -0,0 +1,66 @@
plugins {
`openinv-base`
alias(libs.plugins.shadow)
}
repositories {
mavenLocal()
}
val spigotVer = "1.21.4-R0.1-SNAPSHOT"
rootProject.extra["spigotVersion"] = spigotVer
rootProject.extra["craftbukkitPackage"] = "v1_21_R3"
configurations.all {
resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") {
val spigot = candidates.firstOrNull {
it.id.let {
id -> id is ModuleComponentIdentifier && id.module == "spigot-api"
}
}
if (spigot != null) {
select(spigot)
}
because("module is written for Spigot servers")
}
}
val spigotRemap = configurations.create("spigotRemap")
dependencies {
spigotRemap("net.md-5:SpecialSource:1.11.4:shaded")
compileOnly(libs.spigotapi)
compileOnly(create("org.spigotmc", "spigot", spigotVer, classifier = "remapped-mojang"))
compileOnly(project(":openinvapi"))
compileOnly(project(":openinvcommon"))
// Reduce duplicate code by lightly remapping common adapter.
implementation(project(":openinvadaptercommon", configuration = "spigotRelocated"))
}
tasks.shadowJar {
relocate("com.lishid.openinv.internal.common", "com.lishid.openinv.internal.reobf")
}
val reobfTask = tasks.register<ReobfTask>("reobfTask") {
notCompatibleWithConfigurationCache("gradle is hard")
dependsOn(tasks.shadowJar)
inputFile.value(tasks.shadowJar.get().archiveFile.get())
spigotVersion.value(spigotVer)
}
configurations {
consumable("reobf") {
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
}
}
}
artifacts {
add("reobf", reobfTask)
}
@@ -0,0 +1,194 @@
package com.lishid.openinv.internal.reobf.container;
import com.lishid.openinv.internal.AnySilentContainerBase;
import com.lishid.openinv.internal.reobf.container.menu.OpenChestMenu;
import com.lishid.openinv.internal.reobf.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("The mapping of ServerPlayerGameMode#gameModeForPlayer 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);
}
}
}
@@ -0,0 +1,220 @@
package com.lishid.openinv.internal.reobf.player;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.reobf.container.OpenEnderChest;
import com.lishid.openinv.internal.reobf.container.OpenInventory;
import com.mojang.authlib.GameProfile;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.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 org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_21_R3.CraftServer;
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.logging.Logger;
public class PlayerManager implements com.lishid.openinv.internal.PlayerManager {
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);
return true;
}
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 (!OpenPlayer.isConnected(player.connection)) {
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();
}
}
-77
View File
@@ -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.1</version>
</parent>
<artifactId>openinvadapter1_20_R3</artifactId>
<name>OpenInvAdapter1_20_R3</name>
<properties>
<maven.compiler.system>17</maven.compiler.system>
<maven.compiler.release>17</maven.compiler.release>
<spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${spigot.version}</version>
</dependency>
<dependency>
<artifactId>spigot</artifactId>
<groupId>org.spigotmc</groupId>
<scope>provided</scope>
<version>${spigot.version}</version>
<classifier>remapped-mojang</classifier>
</dependency>
<dependency>
<artifactId>openinvapi</artifactId>
<groupId>com.lishid</groupId>
</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>
@@ -1,208 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.internal.AnySilentContainerBase;
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 = PlayerManager.getContainers(enderChest.getContainerSize());
int rows = enderChest.getContainerSize() / 9;
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
}, Component.translatable("container.enderchest")));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
if (!(blockEntity instanceof MenuProvider menuProvider)) {
return false;
}
BlockState blockState = level.getBlockState(blockPos);
Block block = blockState.getBlock();
if (block instanceof ChestBlock chestBlock) {
// boolean flag: do not check if chest is blocked
menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true);
if (menuProvider == null) {
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);
}
}
}
@@ -1,184 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.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_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_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 {
private static final Set<String> RESET_TAGS = Set.of(
// net.minecraft.world.Entity#saveWithoutId(CompoundTag)
"CustomName",
"CustomNameVisible",
"Silent",
"NoGravity",
"Glowing",
"TicksFrozen",
"HasVisualFire",
"Tags",
"Passengers",
// net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(CompoundTag)
// Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle
"warden_spawn_tracker",
"enteredNetherPosition",
"SpawnX",
"SpawnY",
"SpawnZ",
"SpawnForced",
"SpawnAngle",
"SpawnDimension",
// net.minecraft.world.entity.player.Player#addAdditionalSaveData(CompoundTag)
"ShoulderEntityLeft",
"ShoulderEntityRight",
"LastDeathLocation",
// net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(CompoundTag)
"ActiveEffects", // Backwards compat: Renamed from 1.19
"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.getPlayerData(player.getStringUUID());
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 file = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
NbtIo.writeCompressed(playerData, file);
Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
Util.safeReplaceFile(dataFile, file, backupFile);
} catch (Exception e) {
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
}
}
@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,
@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);
}
}
}
@@ -1,292 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.InventoryViewTitle;
import com.lishid.openinv.internal.OpenInventoryView;
import com.lishid.openinv.util.lang.LanguageManager;
import com.mojang.authlib.GameProfile;
import com.mojang.serialization.Dynamic;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.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 final @NotNull LanguageManager lang;
private @Nullable Field bukkitEntity;
public PlayerManager(@NotNull Logger logger, @NotNull LanguageManager lang) {
this.logger = logger;
this.lang = lang;
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).
);
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);
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 player, @NotNull ISpecialInventory inventory, boolean viewOnly) {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.connection == null) {
return null;
}
InventoryViewTitle title = InventoryViewTitle.of(inventory);
if (title == null) {
return player.openInventory(inventory.getBukkitInventory());
}
InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(lang, player, inventory));
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public MenuType<?> getType() {
return getContainers(inventory.getBukkitInventory().getSize());
}
};
container.setTitle(Component.literal(view.getOriginalTitle()));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
return null;
}
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
Component.literal(container.getBukkitView().getTitle())));
nmsPlayer.containerMenu = container;
nmsPlayer.initMenu(container);
return container.getBukkitView();
}
static @NotNull MenuType<?> getContainers(int inventorySize) {
return switch (inventorySize) {
case 9 -> MenuType.GENERIC_9x1;
case 18 -> MenuType.GENERIC_9x2;
case 36 -> MenuType.GENERIC_9x4; // PLAYER
case 41, 45 -> MenuType.GENERIC_9x5;
case 54 -> MenuType.GENERIC_9x6;
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
};
}
}
@@ -1,351 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R3;
import com.lishid.openinv.internal.ISpecialEnderChest;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.ContainerListener;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
private ServerPlayer owner;
private NonNullList<ItemStack> items;
private boolean playerOnline;
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
super(PlayerManager.getHandle(player));
this.inventory = new CraftInventory(this);
this.owner = PlayerManager.getHandle(player);
this.playerOnline = online;
this.items = this.owner.getEnderChestInventory().items;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
if (this.playerOnline) {
return;
}
ServerPlayer offlinePlayer = this.owner;
ServerPlayer onlinePlayer = PlayerManager.getHandle(player);
// Set owner to new player.
this.owner = onlinePlayer;
// Set player's ender chest contents to our modified contents.
PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory();
for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) {
onlineEnderChest.setItem(i, this.items.get(i));
}
// Set our item array to the new inventory's array.
this.items = onlineEnderChest.items;
// Add viewers to new inventory.
onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction);
this.playerOnline = true;
}
@Override
public @NotNull org.bukkit.entity.Player getPlayer() {
return owner.getBukkitEntity();
}
@Override
public void setChanged() {
this.owner.getEnderChestInventory().setChanged();
}
@Override
public List<ItemStack> getContents() {
return this.items;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.owner.getEnderChestInventory().getViewers();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public void setActiveChest(EnderChestBlockEntity enderChest) {
this.owner.getEnderChestInventory().setActiveChest(enderChest);
}
@Override
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
}
@Override
public int getMaxStackSize() {
return this.owner.getEnderChestInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int i) {
this.owner.getEnderChestInventory().setMaxStackSize(i);
}
@Override
public InventoryHolder getOwner() {
return this.owner.getEnderChestInventory().getOwner();
}
@Override
public @Nullable Location getLocation() {
return null;
}
@Override
public void addListener(ContainerListener listener) {
this.owner.getEnderChestInventory().addListener(listener);
}
@Override
public void removeListener(ContainerListener listener) {
this.owner.getEnderChestInventory().removeListener(listener);
}
@Override
public ItemStack getItem(int i) {
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
}
@Override
public ItemStack removeItem(int i, int j) {
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public ItemStack addItem(ItemStack itemstack) {
ItemStack localItem = itemstack.copy();
this.moveItemToOccupiedSlotsWithSameType(localItem);
if (localItem.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.moveItemToEmptySlots(localItem);
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
}
}
@Override
public boolean canAddItem(ItemStack itemstack) {
for (ItemStack itemstack1 : this.items) {
if (itemstack1.isEmpty() || (ItemStack.isSameItemSameTags(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize())) {
return true;
}
}
return false;
}
private void moveItemToEmptySlots(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (localItem.isEmpty()) {
this.setItem(i, itemstack.copy());
itemstack.setCount(0);
return;
}
}
}
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (ItemStack.isSameItemSameTags(localItem, itemstack)) {
this.moveItemsBetweenStacks(itemstack, localItem);
if (itemstack.isEmpty()) {
return;
}
}
}
}
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
if (j > 0) {
itemstack1.grow(j);
itemstack.shrink(j);
this.setChanged();
}
}
@Override
public ItemStack removeItemNoUpdate(int i) {
ItemStack itemstack = this.items.get(i);
if (itemstack.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.items.set(i, ItemStack.EMPTY);
return itemstack;
}
}
@Override
public void setItem(int i, ItemStack itemstack) {
this.items.set(i, itemstack);
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
this.setChanged();
}
@Override
public int getContainerSize() {
return this.owner.getEnderChestInventory().getContainerSize();
}
@Override
public boolean isEmpty() {
return this.items.stream().allMatch(ItemStack::isEmpty);
}
@Override
public void startOpen(Player player) {
}
@Override
public void stopOpen(Player player) {
}
@Override
public boolean canPlaceItem(int i, ItemStack itemstack) {
return true;
}
@Override
public void clearContent() {
this.items.clear();
this.setChanged();
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountStack(itemstack);
}
}
@Override
public List<ItemStack> removeAllItems() {
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
this.clearContent();
return list;
}
@Override
public ItemStack removeItemType(Item item, int i) {
ItemStack itemstack = new ItemStack(item, 0);
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
ItemStack localItem = this.getItem(j);
if (localItem.getItem().equals(item)) {
int k = i - itemstack.getCount();
ItemStack splitItem = localItem.split(k);
itemstack.grow(splitItem.getCount());
if (itemstack.getCount() == i) {
break;
}
}
}
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public String toString() {
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString();
}
@Override
public void fromTag(ListTag listTag) {
for (int i = 0; i < this.getContainerSize(); ++i) {
this.setItem(i, ItemStack.EMPTY);
}
for (int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
if (j < this.getContainerSize()) {
this.setItem(j, ItemStack.of(compoundTag));
}
}
}
}
@@ -1,807 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R3;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.DamageTypeTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.item.ArmorItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
private boolean playerOnline;
private Player player;
private NonNullList<ItemStack> items;
private NonNullList<ItemStack> armor;
private NonNullList<ItemStack> offhand;
private List<NonNullList<ItemStack>> compartments;
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
super(PlayerManager.getHandle(bukkitPlayer));
this.inventory = new CraftInventory(this);
this.playerOnline = online;
this.player = super.player;
this.selected = player.getInventory().selected;
this.items = this.player.getInventory().items;
this.armor = this.player.getInventory().armor;
this.offhand = this.player.getInventory().offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
}
@Override
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
if (this.playerOnline) {
return;
}
Player offlinePlayer = this.player;
Player onlinePlayer = PlayerManager.getHandle(player);
onlinePlayer.getInventory().transaction.addAll(this.transaction);
// Set owner to new player.
this.player = onlinePlayer;
// Set player's inventory contents to our modified contents.
Inventory onlineInventory = onlinePlayer.getInventory();
for (int i = 0; i < getContainerSize(); ++i) {
onlineInventory.setItem(i, getRawItem(i));
}
onlineInventory.selected = this.selected;
// Set our item arrays to the new inventory's arrays.
this.items = onlineInventory.items;
this.armor = onlineInventory.armor;
this.offhand = onlineInventory.offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
// Add existing viewers to new viewer list.
Inventory offlineInventory = offlinePlayer.getInventory();
// Remove self from listing - player is always a viewer of their own inventory, prevent duplicates.
offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity());
onlineInventory.transaction.addAll(offlineInventory.transaction);
this.playerOnline = true;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return this.inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public @NotNull HumanEntity getPlayer() {
return this.player.getBukkitEntity();
}
private @NotNull ItemStack getRawItem(int i) {
if (i < 0) {
return ItemStack.EMPTY;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
return list.get(i);
}
}
return ItemStack.EMPTY;
}
private void setRawItem(int i, @NotNull ItemStack itemStack) {
if (i < 0) {
return;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
list.set(i, itemStack);
}
}
}
private record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
if (index < items.size()) {
return new IndexedCompartment(items, getReversedItemSlotNum(index));
}
index -= items.size();
if (index < armor.size()) {
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
}
index -= armor.size();
if (index < offhand.size()) {
return new IndexedCompartment(offhand, index);
}
index -= offhand.size();
return new IndexedCompartment(null, index);
}
private int getReversedArmorSlotNum(final int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
private int getReversedItemSlotNum(final int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
private boolean contains(Predicate<ItemStack> predicate) {
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
}
@Override
public List<ItemStack> getArmorContents() {
return this.armor;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.player.getInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.player.getInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.player.getInventory().getViewers();
}
@Override
public InventoryHolder getOwner() {
return this.player.getBukkitEntity();
}
@Override
public int getMaxStackSize() {
return this.player.getInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int size) {
this.player.getInventory().setMaxStackSize(size);
}
@Override
public Location getLocation() {
return this.player.getBukkitEntity().getLocation();
}
@Override
public boolean hasCustomName() {
return false;
}
@Override
public List<ItemStack> getContents() {
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
}
@Override
public ItemStack getSelected() {
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
}
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && ItemStack.isSameItemSameTags(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
@Override
public int canHold(ItemStack itemstack) {
int remains = itemstack.getCount();
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.getRawItem(i);
if (itemstack1.isEmpty()) {
return itemstack.getCount();
}
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.getCount();
}
}
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
}
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
}
@Override
public int getFreeSlot() {
for(int i = 0; i < this.items.size(); ++i) {
if (this.items.get(i).isEmpty()) {
return i;
}
}
return -1;
}
@Override
public void setPickedItem(ItemStack itemstack) {
int i = this.findSlotMatchingItem(itemstack);
if (isHotbarSlot(i)) {
this.selected = i;
} else if (i == -1) {
this.selected = this.getSuitableHotbarSlot();
if (!this.items.get(this.selected).isEmpty()) {
int j = this.getFreeSlot();
if (j != -1) {
this.items.set(j, this.items.get(this.selected));
}
}
this.items.set(this.selected, itemstack);
} else {
this.pickSlot(i);
}
}
@Override
public void pickSlot(int i) {
this.selected = this.getSuitableHotbarSlot();
ItemStack itemstack = this.items.get(this.selected);
this.items.set(this.selected, this.items.get(i));
this.items.set(i, itemstack);
}
@Override
public int findSlotMatchingItem(ItemStack itemstack) {
for(int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemstack, this.items.get(i))) {
return i;
}
}
return -1;
}
@Override
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
for(int i = 0; i < this.items.size(); ++i) {
ItemStack localItem = this.items.get(i);
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameTags(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.hasCustomHoverName()) {
return i;
}
}
return -1;
}
@Override
public int getSuitableHotbarSlot() {
int i;
int j;
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (this.items.get(i).isEmpty()) {
return i;
}
}
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (!this.items.get(i).isEnchanted()) {
return i;
}
}
return this.selected;
}
@Override
public void swapPaint(double d0) {
if (d0 > 0.0D) {
d0 = 1.0D;
}
if (d0 < 0.0D) {
d0 = -1.0D;
}
this.selected = (int) (this.selected - d0);
while (this.selected < 0) {
this.selected += 9;
}
while(this.selected >= 9) {
this.selected -= 9;
}
}
@Override
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
byte b0 = 0;
boolean flag = i == 0;
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
ItemStack itemstack = this.player.containerMenu.getCarried();
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
if (itemstack.isEmpty()) {
this.player.containerMenu.setCarried(ItemStack.EMPTY);
}
return j;
}
private int addResource(ItemStack itemstack) {
int i = this.getSlotWithRemainingSpace(itemstack);
if (i == -1) {
i = this.getFreeSlot();
}
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
}
private int addResource(int i, ItemStack itemstack) {
Item item = itemstack.getItem();
int j = itemstack.getCount();
ItemStack localItemStack = this.getRawItem(i);
if (localItemStack.isEmpty()) {
localItemStack = new ItemStack(item, 0);
if (itemstack.hasTag()) {
// hasTag ensures tag not null
//noinspection ConstantConditions
localItemStack.setTag(itemstack.getTag().copy());
}
this.setRawItem(i, localItemStack);
}
int k = Math.min(j, localItemStack.getMaxStackSize() - localItemStack.getCount());
if (k > this.getMaxStackSize() - localItemStack.getCount()) {
k = this.getMaxStackSize() - localItemStack.getCount();
}
if (k != 0) {
j -= k;
localItemStack.grow(k);
localItemStack.setPopTime(5);
}
return j;
}
@Override
public int getSlotWithRemainingSpace(ItemStack itemstack) {
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
return this.selected;
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
return 40;
} else {
for(int i = 0; i < this.items.size(); ++i) {
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
return i;
}
}
return -1;
}
}
@Override
public void tick() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (!compartment.get(i).isEmpty()) {
compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i);
}
}
}
}
@Override
public boolean add(ItemStack itemStack) {
return this.add(-1, itemStack);
}
@Override
public boolean add(int i, ItemStack itemStack) {
if (itemStack.isEmpty()) {
return false;
} else {
try {
if (itemStack.isDamaged()) {
if (i == -1) {
i = this.getFreeSlot();
}
if (i >= 0) {
this.items.set(i, itemStack.copy());
this.items.get(i).setPopTime(5);
itemStack.setCount(0);
return true;
} else if (this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return false;
}
} else {
int j;
do {
j = itemStack.getCount();
if (i == -1) {
itemStack.setCount(this.addResource(itemStack));
} else {
itemStack.setCount(this.addResource(i, itemStack));
}
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
if (itemStack.getCount() == j && this.player.getAbilities().instabuild) {
itemStack.setCount(0);
return true;
} else {
return itemStack.getCount() < j;
}
}
} catch (Throwable var6) {
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
throw new ReportedException(crashReport);
}
}
}
@Override
public void placeItemBackInInventory(ItemStack itemStack) {
this.placeItemBackInInventory(itemStack, true);
}
@Override
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
while(true) {
if (!itemStack.isEmpty()) {
int i = this.getSlotWithRemainingSpace(itemStack);
if (i == -1) {
i = this.getFreeSlot();
}
if (i != -1) {
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
}
continue;
}
this.player.drop(itemStack, false);
}
return;
}
}
@Override
public ItemStack removeItem(int rawIndex, final int j) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
return ItemStack.EMPTY;
}
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
}
@Override
public void removeItem(ItemStack itemStack) {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (compartment.get(i) == itemStack) {
compartment.set(i, ItemStack.EMPTY);
break;
}
}
}
}
@Override
public ItemStack removeItemNoUpdate(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
if (removed.isEmpty()) {
return ItemStack.EMPTY;
}
return removed;
}
@Override
public void setItem(int rawIndex, final ItemStack itemStack) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
this.player.drop(itemStack, true);
return;
}
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
}
@Override
public float getDestroySpeed(BlockState blockState) {
return this.items.get(this.selected).getDestroySpeed(blockState);
}
@Override
public ListTag save(ListTag listTag) {
for (int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)i);
this.items.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.armor.size(); ++i) {
if (!this.armor.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 100));
this.armor.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.offhand.size(); ++i) {
if (!this.offhand.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 150));
this.offhand.get(i).save(compoundTag);
listTag.add(compoundTag);
}
}
return listTag;
}
@Override
public void load(ListTag listTag) {
this.items.clear();
this.armor.clear();
this.offhand.clear();
for(int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
ItemStack itemstack = ItemStack.of(compoundTag);
if (!itemstack.isEmpty()) {
if (j < this.items.size()) {
this.items.set(j, itemstack);
} else if (j >= 100 && j < this.armor.size() + 100) {
this.armor.set(j - 100, itemstack);
} else if (j >= 150 && j < this.offhand.size() + 150) {
this.offhand.set(j - 150, itemstack);
}
}
}
}
@Override
public int getContainerSize() {
return 45;
}
@Override
public boolean isEmpty() {
return !contains(itemStack -> !itemStack.isEmpty());
}
@Override
public ItemStack getItem(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
return indexedCompartment.compartment().get(indexedCompartment.index());
}
@Override
public Component getName() {
return this.player.getName();
}
@Override
public ItemStack getArmor(int index) {
return this.armor.get(index);
}
@Override
public void hurtArmor(DamageSource damagesource, float damage, int[] armorIndices) {
if (damage > 0.0F) {
damage /= 4.0F;
if (damage < 1.0F) {
damage = 1.0F;
}
for (int index : armorIndices) {
ItemStack itemstack = this.armor.get(index);
if ((!damagesource.is(DamageTypeTags.IS_FIRE) || !itemstack.getItem().isFireResistant()) && itemstack.getItem() instanceof ArmorItem) {
itemstack.hurtAndBreak((int) damage, this.player, localPlayer -> localPlayer.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, index)));
}
}
}
}
@Override
public void dropAll() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
ItemStack itemstack = compartment.get(i);
if (!itemstack.isEmpty()) {
this.player.drop(itemstack, true, false);
compartment.set(i, ItemStack.EMPTY);
}
}
}
}
@Override
public void setChanged() {
super.setChanged();
}
@Override
public int getTimesChanged() {
return super.getTimesChanged();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public boolean contains(ItemStack itemstack) {
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(itemstack.getItem()));
}
@Override
public boolean contains(TagKey<Item> tagKey) {
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey));
}
@Override
public void replaceWith(Inventory inventory) {
Function<Integer, ItemStack> getter;
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
getter = specialPlayerInventory::getRawItem;
} else {
getter = inventory::getItem;
}
for(int i = 0; i < this.getContainerSize(); ++i) {
this.setRawItem(i, getter.apply(i));
}
this.selected = inventory.selected;
}
@Override
public void clearContent() {
for (NonNullList<ItemStack> compartment : this.compartments) {
compartment.clear();
}
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountSimpleStack(itemstack);
}
}
@Override
public ItemStack removeFromSelected(boolean dropWholeStack) {
ItemStack itemstack = this.getSelected();
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
}
}
-77
View File
@@ -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.1</version>
</parent>
<artifactId>openinvadapter1_20_R4</artifactId>
<name>OpenInvAdapter1_20_R4</name>
<properties>
<maven.compiler.system>21</maven.compiler.system>
<maven.compiler.release>21</maven.compiler.release>
<spigot.version>1.20.6-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>
@@ -1,208 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R4;
import com.lishid.openinv.internal.AnySilentContainerBase;
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 = PlayerManager.getContainers(enderChest.getContainerSize());
int rows = enderChest.getContainerSize() / 9;
return new ChestMenu(containers, containerCounter, playerInventory, enderChest, rows);
}, Component.translatable("container.enderchest")));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
if (!(blockEntity instanceof MenuProvider menuProvider)) {
return false;
}
BlockState blockState = level.getBlockState(blockPos);
Block block = blockState.getBlock();
if (block instanceof ChestBlock chestBlock) {
// boolean flag: do not check if chest is blocked
menuProvider = chestBlock.getMenuProvider(blockState, level, blockPos, true);
if (menuProvider == null) {
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);
}
}
}
@@ -1,64 +0,0 @@
package com.lishid.openinv.internal.v1_20_R4;
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.util.lang.LanguageManager;
import net.minecraft.world.Container;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.craftbukkit.v1_20_R4.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.Logger;
public class InternalAccessor implements Accessor {
private final @NotNull PlayerManager manager;
private final @NotNull AnySilentContainer anySilentContainer;
public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
manager = new PlayerManager(logger, lang);
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 SpecialPlayerInventory(player, player.isOnline());
}
@Override
public @NotNull ISpecialEnderChest createEnderChest(@NotNull Player player) {
return new SpecialEnderChest(player, player.isOnline());
}
@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) {}
}
@@ -1,194 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R4;
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_20_R4.CraftServer;
import org.bukkit.craftbukkit.v1_20_R4.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",
// Player#addAdditionalSaveData(CompoundTag)
"ShoulderEntityLeft",
"ShoulderEntityRight",
"LastDeathLocation",
"current_explosion_impact_pos",
// LivingEntity#addAdditionalSaveData(CompoundTag)
"ActiveEffects", // Backwards compat: Renamed from 1.19
"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,
@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);
}
}
}
@@ -1,292 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R4;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.InventoryViewTitle;
import com.lishid.openinv.internal.OpenInventoryView;
import com.lishid.openinv.util.lang.LanguageManager;
import com.mojang.authlib.GameProfile;
import com.mojang.serialization.Dynamic;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R4.CraftServer;
import org.bukkit.craftbukkit.v1_20_R4.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R4.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftContainer;
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 final @NotNull LanguageManager lang;
private @Nullable Field bukkitEntity;
public PlayerManager(@NotNull Logger logger, @NotNull LanguageManager lang) {
this.logger = logger;
this.lang = lang;
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).
);
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 player, @NotNull ISpecialInventory inventory, boolean viewOnly) {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.connection == null) {
return null;
}
InventoryViewTitle title = InventoryViewTitle.of(inventory);
if (title == null) {
return player.openInventory(inventory.getBukkitInventory());
}
InventoryView view = new OpenInventoryView(player, inventory, title.getTitle(lang, player, inventory));
AbstractContainerMenu container = new CraftContainer(view, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public MenuType<?> getType() {
return getContainers(inventory.getBukkitInventory().getSize());
}
};
container.setTitle(Component.literal(view.getOriginalTitle()));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
return null;
}
nmsPlayer.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(),
Component.literal(container.getBukkitView().getTitle())));
nmsPlayer.containerMenu = container;
nmsPlayer.initMenu(container);
return container.getBukkitView();
}
static @NotNull MenuType<?> getContainers(int inventorySize) {
return switch (inventorySize) {
case 9 -> MenuType.GENERIC_9x1;
case 18 -> MenuType.GENERIC_9x2;
case 36 -> MenuType.GENERIC_9x4; // PLAYER
case 41, 45 -> MenuType.GENERIC_9x5;
case 54 -> MenuType.GENERIC_9x6;
default -> MenuType.GENERIC_9x3; // Default 27-slot inventory
};
}
}
@@ -1,352 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R4;
import com.lishid.openinv.internal.ISpecialEnderChest;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.ContainerListener;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.inventory.PlayerEnderChestContainer;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {
private final CraftInventory inventory;
private ServerPlayer owner;
private NonNullList<ItemStack> items;
private boolean playerOnline;
public SpecialEnderChest(final org.bukkit.entity.Player player, final Boolean online) {
super(PlayerManager.getHandle(player));
this.inventory = new CraftInventory(this);
this.owner = PlayerManager.getHandle(player);
this.playerOnline = online;
this.items = this.owner.getEnderChestInventory().items;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public void setPlayerOnline(@NotNull final org.bukkit.entity.Player player) {
if (this.playerOnline) {
return;
}
ServerPlayer offlinePlayer = this.owner;
ServerPlayer onlinePlayer = PlayerManager.getHandle(player);
// Set owner to new player.
this.owner = onlinePlayer;
// Set player's ender chest contents to our modified contents.
PlayerEnderChestContainer onlineEnderChest = onlinePlayer.getEnderChestInventory();
for (int i = 0; i < onlineEnderChest.getContainerSize(); ++i) {
onlineEnderChest.setItem(i, this.items.get(i));
}
// Set our item array to the new inventory's array.
this.items = onlineEnderChest.items;
// Add viewers to new inventory.
onlineEnderChest.transaction.addAll(offlinePlayer.getEnderChestInventory().transaction);
this.playerOnline = true;
}
@Override
public @NotNull org.bukkit.entity.Player getPlayer() {
return owner.getBukkitEntity();
}
@Override
public void setChanged() {
this.owner.getEnderChestInventory().setChanged();
}
@Override
public List<ItemStack> getContents() {
return this.items;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.owner.getEnderChestInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.owner.getEnderChestInventory().getViewers();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public void setActiveChest(EnderChestBlockEntity enderChest) {
this.owner.getEnderChestInventory().setActiveChest(enderChest);
}
@Override
public boolean isActiveChest(EnderChestBlockEntity enderChest) {
return this.owner.getEnderChestInventory().isActiveChest(enderChest);
}
@Override
public int getMaxStackSize() {
return this.owner.getEnderChestInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int i) {
this.owner.getEnderChestInventory().setMaxStackSize(i);
}
@Override
public InventoryHolder getOwner() {
return this.owner.getEnderChestInventory().getOwner();
}
@Override
public @Nullable Location getLocation() {
return null;
}
@Override
public void addListener(ContainerListener listener) {
this.owner.getEnderChestInventory().addListener(listener);
}
@Override
public void removeListener(ContainerListener listener) {
this.owner.getEnderChestInventory().removeListener(listener);
}
@Override
public ItemStack getItem(int i) {
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.EMPTY;
}
@Override
public ItemStack removeItem(int i, int j) {
ItemStack itemstack = ContainerHelper.removeItem(this.items, i, j);
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public ItemStack addItem(ItemStack itemstack) {
ItemStack localItem = itemstack.copy();
this.moveItemToOccupiedSlotsWithSameType(localItem);
if (localItem.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.moveItemToEmptySlots(localItem);
return localItem.isEmpty() ? ItemStack.EMPTY : localItem;
}
}
@Override
public boolean canAddItem(ItemStack itemstack) {
for (ItemStack itemstack1 : this.items) {
if (itemstack1.isEmpty() || (ItemStack.isSameItemSameComponents(itemstack1, itemstack) && itemstack1.getCount() < itemstack1.getMaxStackSize())) {
return true;
}
}
return false;
}
private void moveItemToEmptySlots(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (localItem.isEmpty()) {
this.setItem(i, itemstack.copy());
itemstack.setCount(0);
return;
}
}
}
private void moveItemToOccupiedSlotsWithSameType(ItemStack itemstack) {
for(int i = 0; i < this.getContainerSize(); ++i) {
ItemStack localItem = this.getItem(i);
if (ItemStack.isSameItemSameComponents(localItem, itemstack)) {
this.moveItemsBetweenStacks(itemstack, localItem);
if (itemstack.isEmpty()) {
return;
}
}
}
}
private void moveItemsBetweenStacks(ItemStack itemstack, ItemStack itemstack1) {
int i = Math.min(this.getMaxStackSize(), itemstack1.getMaxStackSize());
int j = Math.min(itemstack.getCount(), i - itemstack1.getCount());
if (j > 0) {
itemstack1.grow(j);
itemstack.shrink(j);
this.setChanged();
}
}
@Override
public ItemStack removeItemNoUpdate(int i) {
ItemStack itemstack = this.items.get(i);
if (itemstack.isEmpty()) {
return ItemStack.EMPTY;
} else {
this.items.set(i, ItemStack.EMPTY);
return itemstack;
}
}
@Override
public void setItem(int i, ItemStack itemstack) {
this.items.set(i, itemstack);
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
this.setChanged();
}
@Override
public int getContainerSize() {
return this.owner.getEnderChestInventory().getContainerSize();
}
@Override
public boolean isEmpty() {
return this.items.stream().allMatch(ItemStack::isEmpty);
}
@Override
public void startOpen(Player player) {
}
@Override
public void stopOpen(Player player) {
}
@Override
public boolean canPlaceItem(int i, ItemStack itemstack) {
return true;
}
@Override
public void clearContent() {
this.items.clear();
this.setChanged();
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountStack(itemstack);
}
}
@Override
public List<ItemStack> removeAllItems() {
List<ItemStack> list = this.items.stream().filter(Predicate.not(ItemStack::isEmpty)).collect(Collectors.toList());
this.clearContent();
return list;
}
@Override
public ItemStack removeItemType(Item item, int i) {
ItemStack itemstack = new ItemStack(item, 0);
for(int j = this.getContainerSize() - 1; j >= 0; --j) {
ItemStack localItem = this.getItem(j);
if (localItem.getItem().equals(item)) {
int k = i - itemstack.getCount();
ItemStack splitItem = localItem.split(k);
itemstack.grow(splitItem.getCount());
if (itemstack.getCount() == i) {
break;
}
}
}
if (!itemstack.isEmpty()) {
this.setChanged();
}
return itemstack;
}
@Override
public String toString() {
return this.items.stream().filter((itemStack) -> !itemStack.isEmpty()).toList().toString();
}
@Override
public void fromTag(ListTag listTag, HolderLookup.Provider holderLookup) {
for (int i = 0; i < this.getContainerSize(); ++i) {
this.setItem(i, ItemStack.EMPTY);
}
for (int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
if (j < this.getContainerSize()) {
this.setItem(j, ItemStack.parseOptional(holderLookup, compoundTag));
}
}
}
}
@@ -1,768 +0,0 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_20_R4;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.core.NonNullList;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.TagKey;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R4.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R4.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SpecialPlayerInventory extends Inventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
private boolean playerOnline;
private Player player;
private NonNullList<ItemStack> items;
private NonNullList<ItemStack> armor;
private NonNullList<ItemStack> offhand;
private List<NonNullList<ItemStack>> compartments;
public SpecialPlayerInventory(@NotNull org.bukkit.entity.Player bukkitPlayer, @NotNull Boolean online) {
super(PlayerManager.getHandle(bukkitPlayer));
this.inventory = new CraftInventory(this);
this.playerOnline = online;
this.player = super.player;
this.selected = player.getInventory().selected;
this.items = this.player.getInventory().items;
this.armor = this.player.getInventory().armor;
this.offhand = this.player.getInventory().offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
}
@Override
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
if (this.playerOnline) {
return;
}
Player offlinePlayer = this.player;
Player onlinePlayer = PlayerManager.getHandle(player);
onlinePlayer.getInventory().transaction.addAll(this.transaction);
// Set owner to new player.
this.player = onlinePlayer;
// Set player's inventory contents to our modified contents.
Inventory onlineInventory = onlinePlayer.getInventory();
for (int i = 0; i < getContainerSize(); ++i) {
onlineInventory.setItem(i, getRawItem(i));
}
onlineInventory.selected = this.selected;
// Set our item arrays to the new inventory's arrays.
this.items = onlineInventory.items;
this.armor = onlineInventory.armor;
this.offhand = onlineInventory.offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand);
// Add existing viewers to new viewer list.
Inventory offlineInventory = offlinePlayer.getInventory();
// Remove self from listing - player is always a viewer of their own inventory, prevent duplicates.
offlineInventory.transaction.remove(offlinePlayer.getBukkitEntity());
onlineInventory.transaction.addAll(offlineInventory.transaction);
this.playerOnline = true;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return this.inventory;
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public @NotNull HumanEntity getPlayer() {
return this.player.getBukkitEntity();
}
private @NotNull ItemStack getRawItem(int i) {
if (i < 0) {
return ItemStack.EMPTY;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
return list.get(i);
}
}
return ItemStack.EMPTY;
}
private void setRawItem(int i, @NotNull ItemStack itemStack) {
if (i < 0) {
return;
}
NonNullList<ItemStack> list;
for (Iterator<NonNullList<ItemStack>> iterator = this.compartments.iterator(); iterator.hasNext(); i -= list.size()) {
list = iterator.next();
if (i < list.size()) {
list.set(i, itemStack);
}
}
}
private record IndexedCompartment(@Nullable NonNullList<ItemStack> compartment, int index) {}
private @NotNull SpecialPlayerInventory.IndexedCompartment getIndexedContent(int index) {
if (index < items.size()) {
return new IndexedCompartment(items, getReversedItemSlotNum(index));
}
index -= items.size();
if (index < armor.size()) {
return new IndexedCompartment(armor, getReversedArmorSlotNum(index));
}
index -= armor.size();
if (index < offhand.size()) {
return new IndexedCompartment(offhand, index);
}
index -= offhand.size();
return new IndexedCompartment(null, index);
}
private int getReversedArmorSlotNum(final int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
private int getReversedItemSlotNum(final int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
@Override
public boolean contains(Predicate<ItemStack> predicate) {
return this.compartments.stream().flatMap(NonNullList::stream).anyMatch(predicate);
}
@Override
public List<ItemStack> getArmorContents() {
return this.armor;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.player.getInventory().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.player.getInventory().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.player.getInventory().getViewers();
}
@Override
public InventoryHolder getOwner() {
return this.player.getBukkitEntity();
}
@Override
public int getMaxStackSize() {
return this.player.getInventory().getMaxStackSize();
}
@Override
public void setMaxStackSize(int size) {
this.player.getInventory().setMaxStackSize(size);
}
@Override
public Location getLocation() {
return this.player.getBukkitEntity().getLocation();
}
@Override
public boolean hasCustomName() {
return false;
}
@Override
public List<ItemStack> getContents() {
return this.compartments.stream().flatMap(Collection::stream).collect(Collectors.toList());
}
@Override
public ItemStack getSelected() {
return isHotbarSlot(this.selected) ? this.items.get(this.selected) : ItemStack.EMPTY;
}
private boolean hasRemainingSpaceForItem(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
@Override
public int canHold(ItemStack itemstack) {
int remains = itemstack.getCount();
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.getRawItem(i);
if (itemstack1.isEmpty()) {
return itemstack.getCount();
}
if (this.hasRemainingSpaceForItem(itemstack1, itemstack)) {
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.getCount();
}
}
ItemStack offhandItemStack = this.getRawItem(this.items.size() + this.armor.size());
if (this.hasRemainingSpaceForItem(offhandItemStack, itemstack)) {
remains -= (offhandItemStack.getMaxStackSize() < this.getMaxStackSize() ? offhandItemStack.getMaxStackSize() : this.getMaxStackSize()) - offhandItemStack.getCount();
}
return remains <= 0 ? itemstack.getCount() : itemstack.getCount() - remains;
}
@Override
public int getFreeSlot() {
for(int i = 0; i < this.items.size(); ++i) {
if (this.items.get(i).isEmpty()) {
return i;
}
}
return -1;
}
@Override
public void setPickedItem(ItemStack itemstack) {
int i = this.findSlotMatchingItem(itemstack);
if (isHotbarSlot(i)) {
this.selected = i;
} else if (i == -1) {
this.selected = this.getSuitableHotbarSlot();
if (!this.items.get(this.selected).isEmpty()) {
int j = this.getFreeSlot();
if (j != -1) {
this.items.set(j, this.items.get(this.selected));
}
}
this.items.set(this.selected, itemstack);
} else {
this.pickSlot(i);
}
}
@Override
public void pickSlot(int i) {
this.selected = this.getSuitableHotbarSlot();
ItemStack itemstack = this.items.get(this.selected);
this.items.set(this.selected, this.items.get(i));
this.items.set(i, itemstack);
}
@Override
public int findSlotMatchingItem(ItemStack itemstack) {
for(int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemstack, this.items.get(i))) {
return i;
}
}
return -1;
}
@Override
public int findSlotMatchingUnusedItem(ItemStack itemStack) {
for(int i = 0; i < this.items.size(); ++i) {
ItemStack localItem = this.items.get(i);
if (!this.items.get(i).isEmpty() && ItemStack.isSameItemSameComponents(itemStack, this.items.get(i)) && !this.items.get(i).isDamaged() && !localItem.isEnchanted() && !localItem.has(DataComponents.CUSTOM_NAME)) {
return i;
}
}
return -1;
}
@Override
public int getSuitableHotbarSlot() {
int i;
int j;
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (this.items.get(i).isEmpty()) {
return i;
}
}
for(j = 0; j < 9; ++j) {
i = (this.selected + j) % 9;
if (!this.items.get(i).isEnchanted()) {
return i;
}
}
return this.selected;
}
@Override
public void swapPaint(double d0) {
int i = (int) Math.signum(d0);
this.selected -= i;
while (this.selected < 0) {
this.selected += 9;
}
while(this.selected >= 9) {
this.selected -= 9;
}
}
@Override
public int clearOrCountMatchingItems(Predicate<ItemStack> predicate, int i, Container container) {
byte b0 = 0;
boolean flag = i == 0;
int j = b0 + ContainerHelper.clearOrCountMatchingItems(this, predicate, i - b0, flag);
j += ContainerHelper.clearOrCountMatchingItems(container, predicate, i - j, flag);
ItemStack itemstack = this.player.containerMenu.getCarried();
j += ContainerHelper.clearOrCountMatchingItems(itemstack, predicate, i - j, flag);
if (itemstack.isEmpty()) {
this.player.containerMenu.setCarried(ItemStack.EMPTY);
}
return j;
}
private int addResource(ItemStack itemstack) {
int i = this.getSlotWithRemainingSpace(itemstack);
if (i == -1) {
i = this.getFreeSlot();
}
return i == -1 ? itemstack.getCount() : this.addResource(i, itemstack);
}
private int addResource(int i, ItemStack itemstack) {
int j = itemstack.getCount();
ItemStack localItemStack = this.getRawItem(i);
if (localItemStack.isEmpty()) {
localItemStack = itemstack.copyWithCount(0);
this.setRawItem(i, localItemStack);
}
int k = Math.min(j, this.getMaxStackSize(localItemStack) - localItemStack.getCount());
if (k != 0) {
j -= k;
localItemStack.grow(k);
localItemStack.setPopTime(5);
}
return j;
}
@Override
public int getSlotWithRemainingSpace(ItemStack itemstack) {
if (this.hasRemainingSpaceForItem(this.getRawItem(this.selected), itemstack)) {
return this.selected;
} else if (this.hasRemainingSpaceForItem(this.getRawItem(40), itemstack)) {
return 40;
} else {
for(int i = 0; i < this.items.size(); ++i) {
if (this.hasRemainingSpaceForItem(this.items.get(i), itemstack)) {
return i;
}
}
return -1;
}
}
@Override
public void tick() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (!compartment.get(i).isEmpty()) {
compartment.get(i).inventoryTick(this.player.level(), this.player, i, this.selected == i);
}
}
}
}
@Override
public boolean add(ItemStack itemStack) {
return this.add(-1, itemStack);
}
@Override
public boolean add(int i, ItemStack itemStack) {
if (itemStack.isEmpty()) {
return false;
} else {
try {
if (itemStack.isDamaged()) {
if (i == -1) {
i = this.getFreeSlot();
}
if (i >= 0) {
this.items.set(i, itemStack.copy());
this.items.get(i).setPopTime(5);
itemStack.setCount(0);
return true;
} else if (this.player.hasInfiniteMaterials()) {
itemStack.setCount(0);
return true;
} else {
return false;
}
} else {
int j;
do {
j = itemStack.getCount();
if (i == -1) {
itemStack.setCount(this.addResource(itemStack));
} else {
itemStack.setCount(this.addResource(i, itemStack));
}
} while(!itemStack.isEmpty() && itemStack.getCount() < j);
if (itemStack.getCount() == j && this.player.hasInfiniteMaterials()) {
itemStack.setCount(0);
return true;
} else {
return itemStack.getCount() < j;
}
}
} catch (Throwable var6) {
CrashReport crashReport = CrashReport.forThrowable(var6, "Adding item to inventory");
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being added");
crashReportCategory.setDetail("Item ID", Item.getId(itemStack.getItem()));
crashReportCategory.setDetail("Item data", itemStack.getDamageValue());
crashReportCategory.setDetail("Item name", () -> itemStack.getHoverName().getString());
throw new ReportedException(crashReport);
}
}
}
@Override
public void placeItemBackInInventory(ItemStack itemStack) {
this.placeItemBackInInventory(itemStack, true);
}
@Override
public void placeItemBackInInventory(ItemStack itemStack, boolean flag) {
while(true) {
if (!itemStack.isEmpty()) {
int i = this.getSlotWithRemainingSpace(itemStack);
if (i == -1) {
i = this.getFreeSlot();
}
if (i != -1) {
int j = itemStack.getMaxStackSize() - this.getRawItem(i).getCount();
if (this.add(i, itemStack.split(j)) && flag && this.player instanceof ServerPlayer) {
((ServerPlayer)this.player).connection.send(new ClientboundContainerSetSlotPacket(-2, 0, i, this.getRawItem(i)));
}
continue;
}
this.player.drop(itemStack, false);
}
return;
}
}
@Override
public ItemStack removeItem(int rawIndex, final int j) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null
|| indexedCompartment.compartment().get(indexedCompartment.index()).isEmpty()) {
return ItemStack.EMPTY;
}
return ContainerHelper.removeItem(indexedCompartment.compartment(), indexedCompartment.index(), j);
}
@Override
public void removeItem(ItemStack itemStack) {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
if (compartment.get(i) == itemStack) {
compartment.set(i, ItemStack.EMPTY);
break;
}
}
}
}
@Override
public ItemStack removeItemNoUpdate(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
ItemStack removed = indexedCompartment.compartment().set(indexedCompartment.index(), ItemStack.EMPTY);
if (removed.isEmpty()) {
return ItemStack.EMPTY;
}
return removed;
}
@Override
public void setItem(int rawIndex, final ItemStack itemStack) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
this.player.drop(itemStack, true);
return;
}
indexedCompartment.compartment().set(indexedCompartment.index(), itemStack);
}
@Override
public float getDestroySpeed(BlockState blockState) {
return this.items.get(this.selected).getDestroySpeed(blockState);
}
@Override
public ListTag save(ListTag listTag) {
for (int i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)i);
this.items.get(i).save(this.player.registryAccess(), compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.armor.size(); ++i) {
if (!this.armor.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 100));
this.armor.get(i).save(this.player.registryAccess(), compoundTag);
listTag.add(compoundTag);
}
}
for (int i = 0; i < this.offhand.size(); ++i) {
if (!this.offhand.get(i).isEmpty()) {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putByte("Slot", (byte)(i + 150));
this.offhand.get(i).save(this.player.registryAccess(), compoundTag);
listTag.add(compoundTag);
}
}
return listTag;
}
@Override
public void load(ListTag listTag) {
this.items.clear();
this.armor.clear();
this.offhand.clear();
for(int i = 0; i < listTag.size(); ++i) {
CompoundTag compoundTag = listTag.getCompound(i);
int j = compoundTag.getByte("Slot") & 255;
ItemStack itemstack = ItemStack.parse(this.player.registryAccess(), compoundTag).orElse(ItemStack.EMPTY);
if (j < this.items.size()) {
this.items.set(j, itemstack);
} else if (j >= 100 && j < this.armor.size() + 100) {
this.armor.set(j - 100, itemstack);
} else if (j >= 150 && j < this.offhand.size() + 150) {
this.offhand.set(j - 150, itemstack);
}
}
}
@Override
public int getContainerSize() {
return 45;
}
@Override
public boolean isEmpty() {
return !contains(itemStack -> !itemStack.isEmpty());
}
@Override
public ItemStack getItem(int rawIndex) {
IndexedCompartment indexedCompartment = getIndexedContent(rawIndex);
if (indexedCompartment.compartment() == null) {
return ItemStack.EMPTY;
}
return indexedCompartment.compartment().get(indexedCompartment.index());
}
@Override
public Component getName() {
return this.player.getName();
}
@Override
public ItemStack getArmor(int index) {
return this.armor.get(index);
}
@Override
public void dropAll() {
for (NonNullList<ItemStack> compartment : this.compartments) {
for (int i = 0; i < compartment.size(); ++i) {
ItemStack itemstack = compartment.get(i);
if (!itemstack.isEmpty()) {
this.player.drop(itemstack, true, false);
compartment.set(i, ItemStack.EMPTY);
}
}
}
}
@Override
public void setChanged() {
super.setChanged();
}
@Override
public int getTimesChanged() {
return super.getTimesChanged();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public boolean contains(ItemStack itemstack) {
return contains(itemStack -> !itemStack.isEmpty() && ItemStack.isSameItemSameComponents(itemstack, itemStack));
}
@Override
public boolean contains(TagKey<Item> tagKey) {
return contains(itemStack -> !itemStack.isEmpty() && itemStack.is(tagKey));
}
@Override
public void replaceWith(Inventory inventory) {
Function<Integer, ItemStack> getter;
if (inventory instanceof SpecialPlayerInventory specialPlayerInventory) {
getter = specialPlayerInventory::getRawItem;
} else {
getter = inventory::getItem;
}
for(int i = 0; i < this.getContainerSize(); ++i) {
this.setRawItem(i, getter.apply(i));
}
this.selected = inventory.selected;
}
@Override
public void clearContent() {
for (NonNullList<ItemStack> compartment : this.compartments) {
compartment.clear();
}
}
@Override
public void fillStackedContents(StackedContents stackedContents) {
for (ItemStack itemstack : this.items) {
stackedContents.accountSimpleStack(itemstack);
}
}
@Override
public ItemStack removeFromSelected(boolean dropWholeStack) {
ItemStack itemstack = this.getSelected();
return itemstack.isEmpty() ? ItemStack.EMPTY : this.removeItem(this.selected, dropWholeStack ? itemstack.getCount() : 1);
}
}
-77
View File
@@ -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.1</version>
</parent>
<artifactId>openinvadapter1_21_R1</artifactId>
<name>OpenInvAdapter1_21_R1</name>
<properties>
<maven.compiler.system>21</maven.compiler.system>
<maven.compiler.release>21</maven.compiler.release>
<spigot.version>1.21.1-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>
+6
View File
@@ -0,0 +1,6 @@
before_install:
- sdk update
- sdk install java 21-tem
- sdk use java 21-tem
install:
- ./gradlew :openinvapi:publishJitpackPublicationToMavenLocal
+43
View File
@@ -0,0 +1,43 @@
plugins {
`openinv-base`
alias(libs.plugins.shadow)
}
repositories {
maven("https://jitpack.io")
}
dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
implementation(project(":openinvadaptercommon"))
implementation(project(":openinvadapterpaper1_21_3"))
implementation(project(":openinvadapterpaper1_21_1"))
implementation(project(":openinvadapterspigot", configuration = "reobf"))
implementation(libs.planarwrappers)
}
tasks.processResources {
expand("version" to version)
}
tasks.jar {
manifest.attributes("paperweight-mappings-namespace" to "mojang")
}
tasks.shadowJar {
minimize {
exclude(":openinv**")
}
}
tasks.register<Copy>("distributePlugin") {
into(rootProject.layout.projectDirectory.dir("dist"))
from(tasks.shadowJar)
rename("openinvplugin.*\\.jar", "OpenInv.jar")
}
tasks.assemble {
dependsOn(tasks.shadowJar)
dependsOn(tasks.named("distributePlugin"))
}
-146
View File
@@ -1,146 +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.1</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_R1</artifactId>
<version>5.1.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.lishid</groupId>
<artifactId>openinvadapter1_20_R4</artifactId>
<version>5.1.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.lishid</groupId>
<artifactId>openinvadapter1_20_R3</artifactId>
<version>5.1.1</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>
@@ -16,8 +16,6 @@
package com.lishid.openinv;
import com.github.jikoo.planarwrappers.util.version.BukkitVersions;
import com.github.jikoo.planarwrappers.util.version.Version;
import com.lishid.openinv.command.ContainerSettingCommand;
import com.lishid.openinv.command.OpenInvCommand;
import com.lishid.openinv.command.SearchContainerCommand;
@@ -28,7 +26,6 @@ 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.LegacyInventoryListener;
import com.lishid.openinv.listener.ToggleListener;
import com.lishid.openinv.util.AccessEqualMode;
import com.lishid.openinv.util.InternalAccessor;
@@ -144,10 +141,6 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
private void registerEvents() {
PluginManager pluginManager = this.getServer().getPluginManager();
// Legacy: extra listener for permission handling and self-view issue prevention.
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21))) {
pluginManager.registerEvents(new LegacyInventoryListener(this, config), this);
}
pluginManager.registerEvents(playerLoader, this);
pluginManager.registerEvents(inventoryManager, this);
pluginManager.registerEvents(new ContainerListener(accessor, languageManager), this);
@@ -179,12 +172,6 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
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");
@@ -257,35 +244,37 @@ public class OpenInv extends JavaPlugin implements IOpenInv {
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_OPEN_OTHER;
edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_EDIT_OTHER;
}
boolean viewOnly = edit != null && !edit.hasPermission(player);
if (!ownContainer && !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 (ownContainer || viewOnly && config.getAccessEqualMode() != AccessEqualMode.DENY) {
return this.accessor.openInventory(player, inventory, viewOnly);
}
// If the player doesn't have an equal access level or equal access is a denial, deny.
if (!player.hasPermission(permission) || config.getAccessEqualMode() == AccessEqualMode.DENY) {
return null;
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;
}
// Since this is a tie, setting decides view state.
if (config.getAccessEqualMode() == AccessEqualMode.VIEW) {
viewOnly = true;
}
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);
@@ -1,234 +0,0 @@
package com.lishid.openinv.listener;
import com.google.errorprone.annotations.Keep;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.util.AccessEqualMode;
import com.lishid.openinv.util.InventoryAccess;
import com.lishid.openinv.util.Permissions;
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.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.inventory.InventoryInteractEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A listener used to enable functionality and prevent issues on versions < 1.21.
*/
public class LegacyInventoryListener implements Listener {
private final @NotNull Plugin plugin;
private final @NotNull Config config;
public LegacyInventoryListener(@NotNull Plugin plugin, @NotNull Config config) {
this.plugin = plugin;
this.config = config;
}
@Keep
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
private void onInventoryClick(@NotNull final InventoryClickEvent event) {
if (handleInventoryInteract(event)) {
return;
}
// Safe cast - has to be a player to be the holder of a special player inventory.
Player player = (Player) event.getWhoClicked();
if (event.getAction() != InventoryAction.MOVE_TO_OTHER_INVENTORY) {
return;
}
// Extra handling for MOVE_TO_OTHER_INVENTORY - apparently Mojang no longer removes the item from the target
// inventory prior to adding it to existing stacks.
ItemStack currentItem = event.getCurrentItem();
if (currentItem == null) {
// Other plugin doing some sort of handling (would be NOTHING for null item otherwise), ignore.
return;
}
ItemStack clone = currentItem.clone();
event.setCurrentItem(null);
// Complete add action in same tick after event completion.
this.plugin.getServer().getScheduler().runTask(this.plugin, () -> player.getInventory().addItem(clone));
}
@Keep
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
private void onInventoryDrag(@NotNull final InventoryDragEvent event) {
if (handleInventoryInteract(event)) {
return;
}
InventoryView view = event.getView();
if (view.getCursor() == null) {
return;
}
int topSize = view.getTopInventory().getSize();
// Get bottom inventory active slots as player inventory slots.
Set<Integer> slots = event.getRawSlots().stream()
.filter(slot -> slot >= topSize)
.map(slot -> convertToPlayerSlot(view, slot)).collect(Collectors.toSet());
int overlapLosses = 0;
// Count overlapping slots.
for (Map.Entry<Integer, ItemStack> newItem : event.getNewItems().entrySet()) {
int rawSlot = newItem.getKey();
// Skip bottom inventory slots.
if (rawSlot >= topSize) {
continue;
}
int convertedSlot = convertToPlayerSlot(view, rawSlot);
if (slots.contains(convertedSlot)) {
overlapLosses += getCountDiff(view.getItem(rawSlot), newItem.getValue());
}
}
// Allow no overlap to proceed as usual.
if (overlapLosses < 1) {
return;
}
final ItemStack lost = view.getCursor().clone();
lost.setAmount(overlapLosses);
// Re-add the lost items in the same tick after the event has completed.
plugin.getServer().getScheduler().runTask(plugin, () -> {
InventoryView currentOpen = event.getWhoClicked().getOpenInventory();
if (!currentOpen.equals(view)) {
event.getWhoClicked().getWorld().dropItem(event.getWhoClicked().getLocation(), lost).setPickupDelay(0);
return;
}
ItemStack cursor = currentOpen.getCursor();
if (cursor == null) {
currentOpen.setCursor(lost);
} else if (lost.isSimilar(cursor)) {
cursor.setAmount(cursor.getAmount() + lost.getAmount());
currentOpen.setCursor(cursor);
} else {
event.getWhoClicked().getWorld().dropItem(event.getWhoClicked().getLocation(), lost).setPickupDelay(0);
}
});
}
private int getCountDiff(@Nullable ItemStack original, @NotNull ItemStack result) {
if (original == null || original.getType() != result.getType()) {
return result.getAmount();
}
return result.getAmount() - original.getAmount();
}
/**
* Handle common InventoryInteractEvent functions.
*
* @param event the InventoryInteractEvent
* @return true unless the top inventory is the holder's own inventory
*/
private boolean handleInventoryInteract(@NotNull final InventoryInteractEvent event) {
HumanEntity viewer = event.getWhoClicked();
Inventory inventory = event.getView().getTopInventory();
ISpecialInventory backing = InventoryAccess.getInventory(inventory);
if (backing == null) {
return true;
}
Permissions edit;
HumanEntity target = backing.getPlayer();
boolean ownContainer = viewer.equals(target);
if (backing instanceof ISpecialPlayerInventory) {
edit = ownContainer ? Permissions.INVENTORY_EDIT_SELF : Permissions.INVENTORY_EDIT_OTHER;
} else if (backing instanceof ISpecialEnderChest) {
edit = ownContainer ? Permissions.ENDERCHEST_EDIT_SELF : Permissions.ENDERCHEST_OPEN_OTHER;
} else {
// Unknown implementation.
return true;
}
if (!edit.hasPermission(viewer)) {
event.setCancelled(true);
return true;
}
// If access ties aren't view-only mode, don't bother with permission checks.
if (config.getAccessEqualMode() != AccessEqualMode.VIEW) {
return !ownContainer || !(backing instanceof ISpecialPlayerInventory);
}
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 (viewer.hasPermission(permission)) {
break;
}
// Otherwise check next access level.
continue;
}
// Either the viewer lacks access (which shouldn't be possible) or this is a tie. View-only.
event.setCancelled(true);
return true;
}
return !ownContainer || !(backing instanceof ISpecialPlayerInventory);
}
private static int convertToPlayerSlot(InventoryView view, int rawSlot) {
int topSize = view.getTopInventory().getSize();
if (topSize <= rawSlot) {
// Slot is not inside special inventory, use Bukkit logic.
return view.convertSlot(rawSlot);
}
// Main inventory, slots 0-26 -> 9-35
if (rawSlot < 27) {
return rawSlot + 9;
}
// Hotbar, slots 27-35 -> 0-8
if (rawSlot < 36) {
return rawSlot - 27;
}
// Armor, slots 36-39 -> 39-36
if (rawSlot < 40) {
return 36 + (39 - rawSlot);
}
// Off-hand
if (rawSlot == 40) {
return 40;
}
// Drop slots, "out of inventory"
return -1;
}
}
@@ -35,19 +35,25 @@ import java.util.logging.Logger;
public class InternalAccessor {
private static final boolean PAPER;
static {
boolean paper = false;
try {
Class.forName("io.papermc.paper.configuration.GlobalConfiguration");
paper = true;
} catch (ClassNotFoundException ignored) {
// Expect remapped server.
}
PAPER = paper;
}
private @Nullable Accessor internal;
public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
try {
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 1))
|| BukkitVersions.MINECRAFT.equals(Version.of(1, 21))) {
internal = new com.lishid.openinv.internal.v1_21_R1.InternalAccessor(logger, lang);
} else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 4))) {
internal = new com.lishid.openinv.internal.v1_20_R3.InternalAccessor(logger, lang);
} else if (BukkitVersions.MINECRAFT.equals(Version.of(1, 20, 6))) {
internal = new com.lishid.openinv.internal.v1_20_R4.InternalAccessor(logger, lang);
}
internal = getAccessor(logger, lang);
if (internal != null) {
InventoryAccess.setProvider(internal::get);
}
@@ -57,6 +63,38 @@ public class InternalAccessor {
}
}
private @Nullable Accessor getAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
Version maxSupported = Version.of(1, 21, 4);
Version minSupported = Version.of(1, 21, 1);
// Ensure version is in supported range.
if (BukkitVersions.MINECRAFT.greaterThan(maxSupported) || BukkitVersions.MINECRAFT.lessThan(minSupported)) {
return null;
}
// Load Spigot accessor.
if (!PAPER) {
if (BukkitVersions.MINECRAFT.equals(maxSupported)) {
// Current Spigot, remapped internals are available.
return new com.lishid.openinv.internal.reobf.InternalAccessor(logger, lang);
} else {
// Older Spigot; unsupported.
return null;
}
}
// Paper or a Paper fork, can use Mojang-mapped internals.
if (BukkitVersions.MINECRAFT.equals(maxSupported)) { // 1.21.4
return new com.lishid.openinv.internal.common.InternalAccessor(logger, lang);
} else if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 21, 3))) {
// 1.21.1-1.21.2 placeholder format
return new com.lishid.openinv.internal.paper1_21_1.InternalAccessor(logger, lang);
}
// 1.21.3
return new com.lishid.openinv.internal.paper1_21_3.InternalAccessor(logger, lang);
}
public String getReleasesLink() {
if (BukkitVersions.MINECRAFT.lessThan(Version.of(1, 4, 4))) { // Good luck.
return "https://dev.bukkit.org/projects/openinv/files?&sort=datecreated";
@@ -121,6 +159,21 @@ public class InternalAccessor {
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 (PAPER && BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 1))) { // Paper 1.21.1-1.21.3
return "https://github.com/Jikoo/OpenInv/releases";
}
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";
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 3))) {
return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.6";
}
return "https://github.com/Jikoo/OpenInv/releases";
}
@@ -111,13 +111,19 @@ public class InventoryManager implements Listener {
@Keep
@EventHandler(priority = EventPriority.LOWEST)
private void onPlayerJoin(@NotNull PlayerJoinEvent event) {
consumeLoaded(event.getPlayer().getUniqueId(), inventory -> checkViewerAccess(inventory, true));
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 -> checkViewerAccess(inventory, false));
consumeLoaded(event.getPlayer().getUniqueId(), inventory -> {
inventory.setPlayerOffline();
checkViewerAccess(inventory, false);
});
}
@Keep
@@ -1,17 +1,22 @@
/*
* Copyright (C) 2011-2021 lishid. All rights reserved.
* This file is an amalgamation of code from the Simmetrics authors.
* The originals may be found here:
* https://github.com/Simmetrics/simmetrics/blob/master/simmetrics-core/src/main/java/org/simmetrics/metrics/JaroWinkler.java
* https://github.com/Simmetrics/simmetrics/blob/master/simmetrics-core/src/main/java/org/simmetrics/metrics/Jaro.java
*
* 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.
* Copyright (C) 2014 - 2016 Simmetrics Authors
*
* 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.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lishid.openinv.util;
+1 -1
View File
@@ -1,6 +1,6 @@
config-version: 8
settings:
equal-access: view
equal-access: allow
command:
open:
no-args-opens-self: false
+2 -2
View File
@@ -1,10 +1,10 @@
name: OpenInv
main: com.lishid.openinv.OpenInv
version: ${project.version}
version: ${version}
author: lishid
authors: [Jikoo, ShadowRanger]
description: Open a player's inventory as a chest and interact with it in real time.
api-version: "1.16"
api-version: "1.13"
permissions:
-221
View File
@@ -1,221 +0,0 @@
<!--
~ Copyright (C) 2011-2023 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<name>OpenInv</name>
<url>http://dev.bukkit.org/bukkit-plugins/openinv/</url>
<modules>
<module>common</module>
</modules>
<version>5.1.1</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.system>17</maven.compiler.system>
<maven.compiler.release>17</maven.compiler.release>
<!-- Silence IDE warning - property is declared in individual internal modules. -->
<spigot.version>unknown</spigot.version>
</properties>
<profiles>
<profile>
<id>all</id>
<modules>
<module>api</module>
<module>addon/togglepersist</module>
<module>common</module>
<module>internal/v1_21_R1</module>
<module>internal/v1_20_R4</module>
<module>internal/v1_20_R3</module>
<module>plugin</module>
</modules>
</profile>
<profile>
<id>api-only</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>api</module>
</modules>
</profile>
</profiles>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencyManagement>
<dependencies>
<dependency>
<artifactId>annotations</artifactId>
<groupId>org.jetbrains</groupId>
<scope>provided</scope>
<version>24.1.0</version>
</dependency>
<dependency>
<artifactId>spigot-api</artifactId>
<groupId>org.spigotmc</groupId>
<scope>provided</scope>
<version>1.20.4-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<artifactId>openinvapi</artifactId>
<groupId>com.lishid</groupId>
<scope>compile</scope>
<version>${project.version}</version>
</dependency>
<dependency>
<artifactId>openinvcommon</artifactId>
<groupId>com.lishid</groupId>
<scope>compile</scope>
<version>${project.version}</version>
</dependency>
<dependency>
<artifactId>planarwrappers</artifactId>
<groupId>com.github.jikoo</groupId>
<scope>compile</scope>
<version>3.2.3</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<!--
~ This plugin is used in scripts to determine if NMS dependencies need to be installed. This must be
~ declared so that we don't run into MDEP-204; the default SuperPOM declares version 2.8.0.
-->
<artifactId>maven-dependency-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.6.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.3.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.4.2</version>
<configuration>
<archive>
<manifestEntries>
<paperweight-mappings-namespace>spigot</paperweight-mappings-namespace>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_core</artifactId>
<version>2.29.2</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-XDcompilePolicy=simple</arg>
<arg>-Xplugin:ErrorProne</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
<arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
<arg>-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED</arg>
</compilerArgs>
<fork>true</fork>
</configuration>
<groupId>org.apache.maven.plugins</groupId>
<version>3.13.0</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>specialsource-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-obf</id>
<configuration>
<srgIn>org.spigotmc:minecraft-server:${spigot.version}:txt:maps-mojang</srgIn>
<reverse>true</reverse>
<remappedDependencies>org.spigotmc:spigot:${spigot.version}:jar:remapped-mojang</remappedDependencies>
<remappedArtifactAttached>true</remappedArtifactAttached>
<remappedClassifierName>remapped-obf</remappedClassifierName>
</configuration>
</execution>
<execution>
<phase>package</phase>
<goals>
<goal>remap</goal>
</goals>
<id>remap-spigot</id>
<configuration>
<inputFile>${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar</inputFile>
<srgIn>org.spigotmc:minecraft-server:${spigot.version}:csrg:maps-spigot</srgIn>
<remappedDependencies>org.spigotmc:spigot:${spigot.version}:jar:remapped-obf</remappedDependencies>
</configuration>
</execution>
</executions>
<groupId>net.md-5</groupId>
<version>2.0.3</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
@@ -1,6 +0,0 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "minecraft:item/empty_armor_slot_chestplate"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 B

+17
View File
@@ -0,0 +1,17 @@
plugins {
`base`
}
tasks.register<Zip>("buildResourcePack") {
archiveFileName = "openinv-legibility-pack.zip"
destinationDirectory = rootProject.layout.projectDirectory.dir("dist")
from("openinv-legibility-pack")
with (copySpec {
include("**/*.json", "**/*.png", "pack.mcmeta")
})
}
tasks.assemble {
dependsOn(tasks.named("buildResourcePack"))
}
@@ -1,7 +1,8 @@
{
"texture_size": [ 16, 32 ],
"textures": {
"layer0": "openinv:item/crafting_output"
"layer0": "openinv:item/crafting_output",
"particle": "minecraft:block/crafting_table_front"
},
"elements": [
{
Loaded 100 of 144 files, more files were not shown because too many files have changed in this diff. Show more