Compare commits

..
72 Commits
Author SHA1 Message Date
Jikoo 8c2ec2121a Bump version to 5.1.10 for release 2025-04-25 11:49:43 -04:00
Adam ad06b46b37 Update to 1.21.5 (#300) 2025-04-25 15:47:20 +00:00
dependabot[bot] 05ac5c92c8 Bump io.papermc.paperweight.userdev from 2.0.0-beta.14 to 2.0.0-beta.16 (#297) 2025-04-01 17:34:11 +00:00
dependabot[bot] 0c8c364c45 Bump org.spigotmc:spigot-api (#298) 2025-04-01 17:33:47 +00:00
Jikoo b89f34915e Hack around JitPack building Spigot
and publishing it, but we don't talk about that
2025-03-21 17:27:54 -04:00
mooviies ada819b30a Fix JitPack API build (#296) 2025-03-19 16:08:13 +00:00
Adam 7a2cef50ec Update SpecialSource to 1.11.5 (#295) 2025-03-11 10:37:32 -04:00
Jikoo 268e91007c Fix array types
In my defense, reading is really hard
2025-03-09 12:55:39 -04:00
Jikoo f4ed616fb8 Consider any class name format valid
This technically allows edge cases where the package (or class, for classes with no package) can start with L and be incorrectly matched, but that feels like a case of play stupid games, win stupid prizes. Classes should be packaged, and package names should be lower case.
Requires a much less hacky change to JarMapping to optimize.
2025-03-09 10:34:18 -04:00
Jikoo 456909966f Reduce remapping maintenance burden
I don't think SpecialSource is likely to make major changes to JarMapping, but RemapperProcessor hasn't been changed in 4 years.
This is less efficient in terms of lookup speed, but does not require the hacky JarMapping extension and doesn't require a second copy of the field mappings to be kept in memory.
2025-03-08 20:58:49 -05:00
Adam 51649bf2ab Remap reflection (#294)
* Hack around SpecialSource limitations with ProGuard mappings
* Relocate PlanarWrappers
* Use libs for shadow exclusion
2025-03-08 17:53:41 -05:00
Adam e44697f856 Parse CurseForge MC versions from release body (#293) 2025-03-07 20:04:17 +00:00
Adam 414b141b37 Improve Gradle build (#290)
* Fix warnings about task not being cacheable
* Add plugin to install Spigot
* Explicitly cache BuildTools products
* Move reobfuscation to a plugin
* Update compilation instructions
2025-03-02 15:55:00 +00:00
Elmar (Cody) BlumeandR00tB33rMan 0db487c6eb Add Folia support (#292)
* feat(folia): added folia support

* chore(folia): apply folia-supported: true label

---------

Co-authored-by: R00tB33rMan <rootb33rman@gmail.com>
2025-02-18 22:26:34 -05:00
Jikoo 3b907e1b37 Bump version to 5.1.10-SNAPSHOT for development 2025-02-14 11:33:47 -05:00
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
Jikoo 5d75640cee Bump version to 5.1.1 for release 2024-08-10 13:33:00 -04:00
Jikoo cace6f6726 Update to 1.21.1
Looks like Spigot builds 1.21.1 for 1.20. Mildly annoying.
2024-08-10 13:32:42 -04:00
Jikoo 1967f4ebb1 Bump version to 5.1.1-SNAPSHOT for development 2024-08-10 12:54:27 -04:00
168 changed files with 5384 additions and 5509 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"
+16 -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,22 @@ jobs:
with:
distribution: 'temurin'
java-version: '21'
cache: 'maven'
# Install Spigot dependencies if necessary.
- name: Install Spigot Dependencies
run: . scripts/install_spigot_dependencies.sh
# We can't use 'maven' prebuilt cache setup because it requires that the project have a pom file.
# BuildTools installs to Maven local if available, so it's easier to just rely on that.
- name: Cache Spigot dependency
uses: actions/cache@v4
with:
path: |
~/.m2/repository/org/spigotmc/
key: ${{ runner.os }}-buildtools-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-buildtools-
- 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 +44,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/*
+8 -17
View File
@@ -12,34 +12,25 @@ 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 }}
name: OpenInv ${{ env.VERSIONED_NAME }}
body: |-
## Supported server versions
**Paper:** TODO VERSION, 1.21.4, 1.21.3, 1.21.1
**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
+6 -12
View File
@@ -36,22 +36,16 @@ 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.
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`
Execute the gradle wrapper:
`./gradlew build`
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).
If you encounter issues with building the Spigot module, try running BuildTools manually.
+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.0</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"
+14
View File
@@ -0,0 +1,14 @@
plugins {
`openinv-base`
`maven-publish`
}
publishing {
publications {
create<MavenPublication>("jitpack") {
groupId = "com.github.Jikoo.OpenInv"
artifactId = "openinvapi"
from(components["java"])
}
}
}
-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.0</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"))
}
+13
View File
@@ -0,0 +1,13 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
mavenCentral()
}
dependencies {
val libs = project.extensions.getByType(VersionCatalogsExtension::class.java).named("libs")
implementation(libs.findLibrary("specialsource").orElseThrow())
}
+7
View File
@@ -0,0 +1,7 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
@@ -0,0 +1,98 @@
package com.github.jikoo.openinv
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.ValueSource
import org.gradle.api.provider.ValueSourceParameters
import org.gradle.process.ExecOperations
import java.io.File
import java.net.URI
import java.nio.file.Files
import javax.inject.Inject
abstract class BuildToolsValueSource: ValueSource<File, BuildToolsValueSource.Parameters> {
@get:Inject
abstract val exec: ExecOperations
interface Parameters: ValueSourceParameters {
val mavenLocal: Property<File>
val workingDir: DirectoryProperty
val spigotVersion: Property<String>
val spigotRevision: Property<String>
val ignoreCached: Property<Boolean>
val javaHome: DirectoryProperty
val javaExecutable: Property<String>
}
override fun obtain(): File {
val version = parameters.spigotVersion.get()
val revision = parameters.spigotRevision.get()
val installLocation = getInstallLocation(version)
// If Spigot is already installed, don't reinstall.
if (!parameters.ignoreCached.get() && installLocation.exists()) {
println("Skipping Spigot installation, $version is present")
return installLocation
}
val buildTools = installBuildTools(parameters.workingDir.get().asFile)
println("Installing Spigot $version (rev $revision)")
exec.javaexec {
environment["JAVA_HOME"] = parameters.javaHome.get()
executable = parameters.javaExecutable.get()
workingDir = buildTools.parentFile
classpath(buildTools)
args = listOf("--nogui", "--rev", revision, "--remapped")
}.rethrowFailure()
// Mark work for delete.
cleanUp(buildTools.parentFile)
if (!installLocation.exists()) {
throw IllegalStateException(
"Failed to install Spigot $version from $revision. Does the revision point to a different version?")
}
return installLocation
}
private fun getInstallLocation(version: String): File {
return parameters.mavenLocal.get().resolve("org/spigotmc/spigot/$version/spigot-$version-remapped-mojang.jar")
}
private fun installBuildTools(workingDir: File): File {
val buildTools = workingDir.resolve("BuildTools.jar")
if (buildTools.exists()) {
return buildTools
}
workingDir.mkdirs()
val buildToolsUrl = "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar"
println("Downloading $buildToolsUrl")
val stream = URI.create(buildToolsUrl).toURL().openStream()
Files.copy(stream, buildTools.toPath())
stream.close()
return buildTools
}
private fun cleanUp(dir: File) {
dir.deleteOnExit()
if (!dir.isDirectory) {
return
}
dir.listFiles()?.forEach {
if (it.isDirectory) {
cleanUp(it)
} else {
it.deleteOnExit()
}
}
}
}
@@ -0,0 +1,21 @@
package com.github.jikoo.openinv
import org.gradle.api.model.ObjectFactory
import org.gradle.jvm.toolchain.JavaToolchainSpec
abstract class SpigotDependencyExtension (
objects: ObjectFactory
) {
val version = objects.property(String::class.java)
val revision = objects.property(String::class.java)
.convention(version.map {
it.replace("-R\\d+\\.\\d+-SNAPSHOT".toRegex(), "")
})
val configuration = objects.property(String::class.java)
val classifier = objects.property(String::class.java).convention("remapped-mojang")
val ext = objects.property(String::class.java)
val java = objects.property(JavaToolchainSpec::class.java)
val ignoreCached = objects.property(Boolean::class.java).convention(false)
}
@@ -0,0 +1,56 @@
package com.github.jikoo.openinv
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.attributes.Bundling
import org.gradle.api.attributes.Category
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.attributes.Usage
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.register
import java.nio.file.Paths
class SpigotReobf: Plugin<Project> {
companion object {
const val ARTIFACT_CONFIG = "reobf"
}
override fun apply(target: Project) {
// Re-use extension from Spigot dependency declaration if available to reduce configuration requirements.
val spigotExt = target.dependencies.extensions.findByType(SpigotDependencyExtension::class.java)
?: target.dependencies.extensions.create(
"spigot",
SpigotDependencyExtension::class.java,
target.objects
)
val mvnLocal = target.repositories.mavenLocal()
val reobfTask = target.tasks.register<SpigotReobfTask>("reobfTask") {
dependsOn(target.tasks.named("shadowJar"))
// ShadowJar extends Jar, so this should be a safe way to get the result without having
// to jump through hoops and shift around shadow declarations in the rest of the project.
inputFile.convention(target.tasks.named<Jar>("shadowJar").get().archiveFile)
spigotVersion.convention(spigotExt.version)
getMavenLocal().set(Paths.get(mvnLocal.url).toFile())
}
// Set up configuration for producing reobf jar.
target.configurations.consumable(ARTIFACT_CONFIG) {
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, target.objects.named(Category.LIBRARY))
attribute(Usage.USAGE_ATTRIBUTE, target.objects.named(Usage.JAVA_RUNTIME))
attribute(Bundling.BUNDLING_ATTRIBUTE, target.objects.named(Bundling.EXTERNAL))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, target.objects.named(LibraryElements.JAR))
}
}
// Add artifact from reobf task.
target.artifacts {
add(ARTIFACT_CONFIG, reobfTask)
}
}
}
@@ -0,0 +1,85 @@
package com.github.jikoo.openinv
import net.md_5.specialsource.Jar
import net.md_5.specialsource.JarMapping
import net.md_5.specialsource.JarRemapper
import net.md_5.specialsource.RemapperProcessor
import net.md_5.specialsource.provider.JarProvider
import net.md_5.specialsource.provider.JointProvider
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.TaskAction
import java.io.File
abstract class SpigotReobfTask: org.gradle.api.tasks.bundling.Jar() {
@get:Input
val spigotVersion: Property<String> = objectFactory.property(String::class.java)
@get:InputFile
val inputFile: RegularFileProperty = objectFactory.fileProperty()
@get:Input
val intermediaryClassifier: Property<String> = objectFactory.property(String::class.java).convention("mojang-mapped")
private val mavenLocal: Property<File> = objectFactory.property(File::class.java)
init {
archiveClassifier.convention(SpigotReobf.ARTIFACT_CONFIG)
}
@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 repo = mavenLocal.get()
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(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(obfServer, spigotMappings, obfPath, archiveFile.get().asFile, false)
}
private fun remapPartial(server: File, mapping: File, input: File, output: File, reverse: Boolean) {
val jarMapping = JarMapping()
jarMapping.loadMappings(mapping.path, reverse, false, null, null)
val inheritance = JointProvider()
jarMapping.setFallbackInheritanceProvider(inheritance)
// Equivalent of --live with server jar on classpath.
val serverJar = Jar.init(server)
inheritance.add(JarProvider(serverJar))
val inputJar = Jar.init(input)
inheritance.add(JarProvider(inputJar))
// Remap reflective access.
val preprocessor = RemapperProcessor(null, jarMapping, null)
val remapper = JarRemapper(preprocessor, jarMapping, null)
remapper.remapJar(inputJar, output, emptySet())
serverJar.close()
inputJar.close()
}
@Internal
internal fun getMavenLocal(): Property<File> {
return mavenLocal
}
}
@@ -0,0 +1,59 @@
package com.github.jikoo.openinv
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.kotlin.dsl.create
import java.nio.file.Paths
import javax.inject.Inject
abstract class SpigotSetup: Plugin<Project> {
@get:Inject
abstract val javaToolchainService: JavaToolchainService
override fun apply(target: Project) {
target.plugins.apply("java")
// Set up extension for configuring Spigot dependency.
val spigotExt = target.dependencies.extensions.findByType(SpigotDependencyExtension::class.java)
?: target.dependencies.extensions.create(
"spigot",
SpigotDependencyExtension::class.java,
target.objects
)
val mvnLocal = target.repositories.mavenLocal()
target.afterEvaluate {
// Get Java requirements, defaulting to version used for compilation.
spigotExt.java.convention(target.extensions.getByType(JavaPluginExtension::class.java).toolchain)
val launcher = javaToolchainService.launcherFor(spigotExt.java.get()).get()
// Install Spigot with BuildTools.
target.providers.of(BuildToolsValueSource::class.java) {
parameters {
mavenLocal.set(Paths.get(mvnLocal.url).toFile())
workingDir.set(target.layout.buildDirectory.dir("tmp/buildtools"))
spigotVersion.set(spigotExt.version)
spigotRevision.set(spigotExt.revision)
ignoreCached.set(spigotExt.ignoreCached)
javaHome.set(launcher.metadata.installationPath)
javaExecutable.set(launcher.executablePath.asFile.path)
}
}.get()
// Add Spigot dependency.
val dependency = target.dependencies.create(
"org.spigotmc",
"spigot",
spigotExt.version.get(),
spigotExt.configuration.orNull,
spigotExt.classifier.orNull,
spigotExt.ext.orNull)
target.dependencies.add("compileOnly", dependency)
}
}
}
@@ -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.0</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.10
description=A Bukkit plugin for opening normally-inaccessible inventories.
# Gradle configuration
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
+19
View File
@@ -0,0 +1,19 @@
[versions]
spigotapi = "1.21.5-R0.1-SNAPSHOT"
specialsource = "1.11.5"
planarwrappers = "3.3.0"
annotations = "26.0.2"
paperweight = "2.0.0-beta.16"
shadow = "8.3.6"
folia-scheduler-wrapper = "v0.0.3"
[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" }
folia-scheduler-wrapper = { module = "com.github.NahuLD.folia-scheduler-wrapper:folia-scheduler-wrapper", version.ref = "folia-scheduler-wrapper" }
[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.5-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,22 @@
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 it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.ChatFormatting;
import net.minecraft.core.NonNullList;
import net.minecraft.network.chat.Component;
@@ -29,21 +31,23 @@ 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;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
public class OpenInventory implements Container, InternalOwned<ServerPlayer>, ISpecialPlayerInventory {
private final List<Content> slots;
protected final List<Content> slots;
private final int size;
private ServerPlayer owner;
protected ServerPlayer owner;
private int maxStackSize = 99;
private CraftInventory bukkitEntity;
public List<HumanEntity> transaction = new ArrayList<>();
@@ -59,15 +63,14 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
setupSlots();
}
private void setupSlots() {
protected void setupSlots() {
// Top of inventory: Regular contents.
int nextIndex = addMainInventory();
// If inventory is expected size, we can arrange slots to be pretty.
Inventory ownerInv = owner.getInventory();
if (ownerInv.items.size() == 36
&& ownerInv.armor.size() == 4
&& ownerInv.offhand.size() == 1
if (ownerInv.getNonEquipmentItems().size() == 36
&& Inventory.EQUIPMENT_SLOT_MAPPING.size() == 5
&& owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) {
// Armor slots: Bottom left.
addArmor(36);
@@ -94,7 +97,7 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
}
private int addMainInventory() {
int listSize = owner.getInventory().items.size();
int listSize = owner.getInventory().getNonEquipmentItems().size();
// Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory,
// so everything else needs to move up a row.
int hotbarDiff = listSize - 9;
@@ -112,7 +115,7 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
slots.set(localIndex, new ContentList(owner, invIndex, type) {
@Override
public void setHolder(@NotNull ServerPlayer holder) {
items = holder.getInventory().items;
items = holder.getInventory().getNonEquipmentItems();
}
});
}
@@ -120,50 +123,36 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
}
private int addArmor(int startIndex) {
int listSize = owner.getInventory().armor.size();
for (int i = 0; i < listSize; ++i) {
// Armor slots go bottom to top; boots are slot 0, helmet is slot 3.
// Since we have to display horizontally due to space restrictions,
// making the left side the "top" is more user-friendly.
int armorIndex;
EquipmentSlot slot;
switch (i) {
case 3 -> {
armorIndex = 0;
slot = EquipmentSlot.FEET;
}
case 2 -> {
armorIndex = 1;
slot = EquipmentSlot.LEGS;
}
case 1 -> {
armorIndex = 2;
slot = EquipmentSlot.CHEST;
}
case 0 -> {
armorIndex = 3;
slot = EquipmentSlot.HEAD;
}
default -> {
// In the event that new armor slots are added, they can be placed at the end.
armorIndex = i;
slot = EquipmentSlot.MAINHAND;
}
// Armor slots go bottom to top; boots are first and helmet is last.
// Since we have to display horizontally due to space restrictions,
// making the left side the "top" is more user-friendly.
EquipmentSlot[] sorted = Inventory.EQUIPMENT_SLOT_MAPPING.int2ObjectEntrySet()
.stream()
.sorted(Comparator.comparingInt(Int2ObjectMap.Entry::getIntKey))
.map(Map.Entry::getValue)
.toArray(EquipmentSlot[]::new);
int localIndex = 0;
for (int i = sorted.length - 1; i >= 0; --i) {
// Skip off-hand, handled separately.
if (sorted[i] == EquipmentSlot.OFFHAND) {
continue;
}
slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot));
slots.set(startIndex + localIndex, new ContentEquipment(owner, sorted[i]));
++localIndex;
}
return startIndex + listSize;
return startIndex + localIndex;
}
private int addOffHand(int startIndex) {
int listSize = owner.getInventory().offhand.size();
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex));
// No off-hand?
if (!Inventory.EQUIPMENT_SLOT_MAPPING.containsValue(EquipmentSlot.OFFHAND)) {
return startIndex;
}
return startIndex + listSize;
slots.set(startIndex, new ContentOffHand(owner));
return startIndex + 1;
}
private int addCrafting(int startIndex, boolean pretty) {
@@ -191,12 +180,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);
}
@@ -275,22 +268,22 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
}
@Override
public ItemStack getItem(int index) {
public @NotNull ItemStack getItem(int index) {
return slots.get(index).get();
}
@Override
public ItemStack removeItem(int index, int amount) {
public @NotNull ItemStack removeItem(int index, int amount) {
return slots.get(index).removePartial(amount);
}
@Override
public ItemStack removeItemNoUpdate(int index) {
public @NotNull ItemStack removeItemNoUpdate(int index) {
return slots.get(index).remove();
}
@Override
public void setItem(int index, ItemStack itemStack) {
public void setItem(int index, @NotNull ItemStack itemStack) {
slots.get(index).set(itemStack);
}
@@ -308,12 +301,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));
@@ -322,22 +315,22 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
}
@Override
public void onOpen(CraftHumanEntity viewer) {
public void onOpen(@NotNull CraftHumanEntity viewer) {
transaction.add(viewer);
}
@Override
public void onClose(CraftHumanEntity viewer) {
public void onClose(@NotNull CraftHumanEntity viewer) {
transaction.remove(viewer);
}
@Override
public List<HumanEntity> getViewers() {
public @NotNull List<HumanEntity> getViewers() {
return transaction;
}
@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 @NotNull [] getArmorContents() {
return new ItemStack[4];
}
@Override
public @NotNull ItemStack @NotNull [] 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 @NotNull ItemStack getItem(@NotNull EquipmentSlot slot) {
return new ItemStack(Material.AIR);
}
@Override
public void setArmorContents(ItemStack @NotNull [] items) {
}
@Override
public void setExtraContents(ItemStack @NotNull [] 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) {
}
}
@@ -0,0 +1,226 @@
package com.lishid.openinv.internal.common.container.bukkit;
import com.google.common.base.Preconditions;
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.inventory.CraftInventory;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class OpenPlayerInventory extends CraftInventory implements PlayerInventory {
public OpenPlayerInventory(@NotNull OpenInventory inventory) {
super(inventory);
}
@Override
public @NotNull OpenInventory getInventory() {
return (OpenInventory) super.getInventory();
}
@Override
public ItemStack @NotNull [] getContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents());
}
@Override
public void setContents(ItemStack[] items) {
Inventory internal = getInventory().getOwnerHandle().getInventory();
int size = internal.getContainerSize();
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
for (int index = 0; index < size; ++index) {
if (index < items.length) {
internal.setItem(index, CraftItemStack.asNMSCopy(items[index]));
} else {
internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY);
}
}
}
@Override
public ItemStack @NotNull [] getStorageContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getNonEquipmentItems());
}
@Override
public void setStorageContents(ItemStack[] items) throws IllegalArgumentException {
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().getNonEquipmentItems();
int size = list.size();
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
for (int index = 0; index < items.length; ++index) {
list.set(index, CraftItemStack.asNMSCopy(items[index]));
}
}
@Override
public @NotNull InventoryType getType() {
return InventoryType.PLAYER;
}
@Override
public @NotNull Player getHolder() {
return getInventory().getOwner();
}
@Override
public @NotNull ItemStack @NotNull [] getArmorContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmorContents());
}
@Override
public void setArmorContents(ItemStack @NotNull [] items) {
int size = Inventory.EQUIPMENT_SLOTS_SORTED_BY_INDEX.length;
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
for (int index = 0; index < items.length; ++index) {
getInventory().getOwnerHandle().getInventory().equipment.set(
Inventory.EQUIPMENT_SLOTS_SORTED_BY_INDEX[index],
CraftItemStack.asNMSCopy(items[index]));
}
}
@Override
public @NotNull ItemStack @NotNull [] getExtraContents() {
return asCraftMirror(List.of(getInventory().getOwnerHandle().getInventory().equipment.get(EquipmentSlot.OFFHAND)));
}
@Override
public void setExtraContents(ItemStack @NotNull [] items) {
Preconditions.checkArgument(items.length <= 1, "items.length must be <= 1");
for (ItemStack item : items) {
getInventory().getOwnerHandle().getInventory().equipment.set(EquipmentSlot.OFFHAND, CraftItemStack.asNMSCopy(item));
}
}
@Override
public @NotNull ItemStack getHelmet() {
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().equipment
.get(EquipmentSlot.HEAD));
}
@Override
public void setHelmet(@Nullable ItemStack helmet) {
getInventory().getOwnerHandle().getInventory().equipment
.set(EquipmentSlot.HEAD, CraftItemStack.asNMSCopy(helmet));
}
@Override
public @NotNull ItemStack getChestplate() {
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().equipment
.get(EquipmentSlot.CHEST));
}
@Override
public void setChestplate(@Nullable ItemStack chestplate) {
getInventory().getOwnerHandle().getInventory().equipment
.set(EquipmentSlot.CHEST, CraftItemStack.asNMSCopy(chestplate));
}
@Override
public @NotNull ItemStack getLeggings() {
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().equipment
.get(EquipmentSlot.LEGS));
}
@Override
public void setLeggings(@Nullable ItemStack leggings) {
getInventory().getOwnerHandle().getInventory().equipment
.set(EquipmentSlot.LEGS, CraftItemStack.asNMSCopy(leggings));
}
@Override
public @NotNull ItemStack getBoots() {
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().equipment
.get(EquipmentSlot.FEET));
}
@Override
public void setBoots(@Nullable ItemStack boots) {
getInventory().getOwnerHandle().getInventory().equipment
.set(EquipmentSlot.FEET, CraftItemStack.asNMSCopy(boots));
}
@Override
public @NotNull ItemStack getItemInMainHand() {
Inventory internal = getInventory().getOwnerHandle().getInventory();
return CraftItemStack.asCraftMirror(internal.getSelectedItem());
}
@Override
public void setItemInMainHand(@Nullable ItemStack item) {
Inventory internal = getInventory().getOwnerHandle().getInventory();
internal.setSelectedItem(CraftItemStack.asNMSCopy(item));
}
@Override
public @NotNull ItemStack getItemInOffHand() {
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory().equipment
.get(EquipmentSlot.OFFHAND));
}
@Override
public void setItemInOffHand(@Nullable ItemStack item) {
getInventory().getOwnerHandle().getInventory().equipment
.set(EquipmentSlot.OFFHAND, CraftItemStack.asNMSCopy(item));
}
@Deprecated
@Override
public @NotNull ItemStack getItemInHand() {
return getItemInMainHand();
}
@Deprecated
@Override
public void setItemInHand(@Nullable ItemStack stack) {
setItemInMainHand(stack);
}
@Override
public int getHeldItemSlot() {
Inventory internal = getInventory().getOwnerHandle().getInventory();
return internal.getNonEquipmentItems().size() - 9 + internal.getSelectedSlot();
}
@Override
public void setHeldItemSlot(int slot) {
slot %= 9;
getInventory().getOwnerHandle().getInventory().setSelectedSlot(slot);
}
@Override
public @NotNull ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) {
return switch (slot) {
case HAND -> getItemInMainHand();
case OFF_HAND -> getItemInOffHand();
case FEET -> getBoots();
case LEGS -> getLeggings();
case CHEST -> getChestplate();
case HEAD -> getHelmet();
default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot);
};
}
@Override
public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) {
switch (slot) {
case HAND -> setItemInMainHand(item);
case OFF_HAND -> setItemInOffHand(item);
case FEET -> setBoots(item);
case LEGS -> setLeggings(item);
case CHEST -> setChestplate(item);
case HEAD -> setHelmet(item);
default -> throw new IllegalArgumentException("Unsupported EquipmentSlot " + slot);
}
}
}
@@ -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;
@@ -0,0 +1,487 @@
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.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.network.HashedStack;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.inventory.ContainerListener;
import net.minecraft.world.inventory.ContainerSynchronizer;
import net.minecraft.world.inventory.DataSlot;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.RemoteSlot;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
/**
* An extension of {@link AbstractContainerMenu} that supports {@link SlotPlaceholder placeholders}.
*/
public abstract class OpenChestMenu<T extends Container & ISpecialInventory & InternalOwned<ServerPlayer>>
extends AbstractContainerMenu {
protected static final int BOTTOM_INVENTORY_SIZE = 36;
protected final T container;
protected final ServerPlayer viewer;
protected final boolean viewOnly;
protected final boolean ownContainer;
protected final int topSize;
private CraftInventoryView<OpenChestMenu<T>, Inventory> bukkitEntity;
// Syncher fields
protected @Nullable ContainerSynchronizer synchronizer;
protected final List<DataSlot> dataSlots = new ArrayList<>();
protected final IntList remoteDataSlots = new IntArrayList();
protected final List<ContainerListener> containerListeners = new ArrayList<>();
private RemoteSlot remoteCarried = RemoteSlot.PLACEHOLDER;
protected boolean suppressRemoteUpdates;
protected OpenChestMenu(
@NotNull MenuType<ChestMenu> type,
int containerCounter,
@NotNull T container,
@NotNull ServerPlayer viewer,
boolean viewOnly) {
super(type, containerCounter);
this.container = container;
this.viewer = viewer;
this.viewOnly = viewOnly;
ownContainer = container.getOwnerHandle().equals(viewer);
topSize = getTopSize(viewer);
preSlotSetup();
int upperRows = topSize / 9;
// View's upper inventory - our container
for (int row = 0; row < upperRows; ++row) {
for (int col = 0; col < 9; ++col) {
// x and y for client purposes, but hey, we're thorough here.
// Adapted from net.minecraft.world.inventory.ChestMenu
int x = 8 + col * 18;
int y = 18 + row * 18;
int index = row * 9 + col;
// Guard against weird inventory sizes.
if (index >= container.getContainerSize()) {
addSlot(new SlotViewOnly(container, index, x, y));
continue;
}
Slot slot = getUpperSlot(index, x, y);
addSlot(slot);
}
}
// View's lower inventory - viewer inventory
int playerInvPad = (upperRows - 4) * 18;
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 9; ++col) {
int x = 8 + col * 18;
int y = playerInvPad + row * 18 + 103;
addSlot(new Slot(viewer.getInventory(), row * 9 + col + 9, x, y));
}
}
// Hotbar
for (int col = 0; col < 9; ++col) {
int x = 8 + col * 18;
int y = playerInvPad + 161;
addSlot(new Slot(viewer.getInventory(), col, x, y));
}
}
public static @NotNull MenuType<ChestMenu> getChestMenuType(int inventorySize) {
inventorySize = ((int) Math.ceil(inventorySize / 9.0)) * 9;
return switch (inventorySize) {
case 9 -> MenuType.GENERIC_9x1;
case 18 -> MenuType.GENERIC_9x2;
case 27 -> MenuType.GENERIC_9x3;
case 36 -> MenuType.GENERIC_9x4;
case 45 -> MenuType.GENERIC_9x5;
case 54 -> MenuType.GENERIC_9x6;
default -> throw new IllegalArgumentException("Inventory size unsupported: " + inventorySize);
};
}
protected void preSlotSetup() {}
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
Slot slot = new Slot(container, index, x, y);
if (viewOnly) {
return SlotViewOnly.wrap(slot);
}
return slot;
}
@Override
public final @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> getBukkitView() {
if (bukkitEntity == null) {
bukkitEntity = createBukkitEntity();
}
return bukkitEntity;
}
protected @NotNull CraftInventoryView<OpenChestMenu<T>, Inventory> createBukkitEntity() {
Inventory top;
if (viewOnly) {
top = new OpenDummyInventory(container, container.getBukkitType());
} else {
top = container.getBukkitInventory();
}
return new CraftInventoryView<>(viewer.getBukkitEntity(), top, this) {
@Override
public @Nullable Inventory getInventory(int rawSlot) {
if (viewOnly) {
return null;
}
return super.getInventory(rawSlot);
}
@Override
public int convertSlot(int rawSlot) {
if (viewOnly) {
return InventoryView.OUTSIDE;
}
return super.convertSlot(rawSlot);
}
@Override
public @NotNull InventoryType.SlotType getSlotType(int slot) {
if (viewOnly) {
return InventoryType.SlotType.OUTSIDE;
}
return super.getSlotType(slot);
}
};
}
private int getTopSize(ServerPlayer viewer) {
MenuType<?> menuType = getType();
if (menuType == MenuType.GENERIC_9x1) {
return 9;
} else if (menuType == MenuType.GENERIC_9x2) {
return 18;
} else if (menuType == MenuType.GENERIC_9x3) {
return 27;
} else if (menuType == MenuType.GENERIC_9x4) {
return 36;
} else if (menuType == MenuType.GENERIC_9x5) {
return 45;
} else if (menuType == MenuType.GENERIC_9x6) {
return 54;
}
// This is a bit gross, but allows us a safe fallthrough.
return menuType.create(-1, viewer.getInventory()).slots.size() - BOTTOM_INVENTORY_SIZE;
}
/**
* Reimplementation of {@link AbstractContainerMenu#moveItemStackTo(ItemStack, int, int, boolean)} that ignores fake
* slots and respects {@link Slot#hasItem()}.
*
* @param itemStack the stack to quick-move
* @param rangeLow the start of the range of slots that can be moved to, inclusive
* @param rangeHigh the end of the range of slots that can be moved to, exclusive
* @param topDown whether to start at the top of the range or bottom
* @return whether the stack was modified as a result of being quick-moved
*/
@Override
protected boolean moveItemStackTo(ItemStack itemStack, int rangeLow, int rangeHigh, boolean topDown) {
boolean modified = false;
boolean stackable = itemStack.isStackable();
Slot firstEmpty = null;
for (int index = topDown ? rangeHigh - 1 : rangeLow;
!itemStack.isEmpty() && (topDown ? index >= rangeLow : index < rangeHigh);
index += topDown ? -1 : 1
) {
Slot slot = slots.get(index);
// If the slot cannot be added to, check the next slot.
if (slot.isFake() || !slot.mayPlace(itemStack)) {
continue;
}
if (slot.hasItem()) {
// If the item isn't stackable, check the next slot.
if (!stackable) {
continue;
}
// Otherwise, add as many as we can from our stack to the slot.
modified |= addToExistingStack(itemStack, slot);
} else {
// If this is the first empty slot, keep track of it for later use.
if (firstEmpty == null) {
firstEmpty = slot;
}
// If the item isn't stackable, we've located the slot we're adding it to, so we're done.
if (!stackable) {
break;
}
}
}
// If the item hasn't been fully added yet, add as many as we can to the first open slot.
if (!itemStack.isEmpty() && firstEmpty != null) {
firstEmpty.setByPlayer(itemStack.split(Math.min(itemStack.getCount(), firstEmpty.getMaxStackSize(itemStack))));
firstEmpty.setChanged();
modified = true;
}
return modified;
}
private static boolean addToExistingStack(ItemStack itemStack, Slot slot) {
ItemStack existing = slot.getItem();
// If the items aren't the same, we can't add our item.
if (!ItemStack.isSameItemSameComponents(itemStack, existing)) {
return false;
}
int max = slot.getMaxStackSize(existing);
int existingCount = existing.getCount();
// If the stack is already full, we can't add more.
if (existingCount >= max) {
return false;
}
int total = existingCount + itemStack.getCount();
// If the existing item can accept the entirety of our item, we're done!
if (total <= max) {
itemStack.setCount(0);
existing.setCount(total);
slot.setChanged();
return true;
}
// Otherwise, add as many as we can.
itemStack.shrink(max - existingCount);
existing.setCount(max);
slot.setChanged();
return true;
}
@Override
public void clicked(int i, int j, @NotNull ClickType clickType, @NotNull Player player) {
if (viewOnly) {
if (clickType == ClickType.QUICK_CRAFT) {
sendAllDataToRemote();
}
return;
}
super.clicked(i, j, clickType, player);
}
@Override
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 @NotNull Slot addSlot(@NotNull Slot slot) {
slot.index = this.slots.size();
this.slots.add(slot);
this.lastSlots.add(ItemStack.EMPTY);
this.remoteSlots.add(this.synchronizer != null ? this.synchronizer.createSlot() : RemoteSlot.PLACEHOLDER);
return slot;
}
@Override
protected @NotNull DataSlot addDataSlot(@NotNull DataSlot dataSlot) {
this.dataSlots.add(dataSlot);
this.remoteDataSlots.add(0);
return dataSlot;
}
@Override
protected void addDataSlots(ContainerData containerData) {
for (int i = 0; i < containerData.getCount(); i++) {
this.addDataSlot(DataSlot.forContainer(containerData, i));
}
}
@Override
public void addSlotListener(@NotNull ContainerListener containerListener) {
if (!this.containerListeners.contains(containerListener)) {
this.containerListeners.add(containerListener);
this.broadcastChanges();
}
}
@Override
public void setSynchronizer(@NotNull ContainerSynchronizer containerSynchronizer) {
this.synchronizer = containerSynchronizer;
this.remoteCarried = synchronizer.createSlot();
this.remoteSlots.replaceAll(slot -> synchronizer.createSlot());
this.sendAllDataToRemote();
}
@Override
public void sendAllDataToRemote() {
List<ItemStack> contentsCopy = new ArrayList<>();
for (int index = 0; index < slots.size(); ++index) {
Slot slot = slots.get(index);
ItemStack itemStack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem();
contentsCopy.add(itemStack);
this.remoteSlots.get(index).force(itemStack);
}
remoteCarried.force(getCarried());
for (int index = 0; index < this.dataSlots.size(); ++index) {
this.remoteDataSlots.set(index, this.dataSlots.get(index).get());
}
if (this.synchronizer != null) {
this.synchronizer.sendInitialData(this, contentsCopy, this.getCarried().copy(), this.remoteDataSlots.toIntArray());
}
}
@Override
public void broadcastCarriedItem() {
ItemStack carried = this.getCarried();
this.remoteCarried.force(carried);
if (this.synchronizer != null) {
this.synchronizer.sendCarriedChange(this, carried.copy());
}
}
@Override
public void removeSlotListener(@NotNull ContainerListener containerListener) {
this.containerListeners.remove(containerListener);
}
@Override
public void broadcastChanges() {
for (int index = 0; index < this.slots.size(); ++index) {
Slot slot = this.slots.get(index);
ItemStack itemstack = slot instanceof SlotPlaceholder placeholder ? placeholder.getOrDefault() : slot.getItem();
Supplier<ItemStack> supplier = Suppliers.memoize(itemstack::copy);
this.triggerSlotListeners(index, itemstack, supplier);
this.synchronizeSlotToRemote(index, itemstack, supplier);
}
this.synchronizeCarriedToRemote();
for (int index = 0; index < this.dataSlots.size(); ++index) {
DataSlot dataSlot = this.dataSlots.get(index);
int j = dataSlot.get();
if (dataSlot.checkAndClearUpdateFlag()) {
this.updateDataSlotListeners(index, j);
}
this.synchronizeDataSlotToRemote(index, j);
}
}
@Override
public void broadcastFullState() {
for (int index = 0; index < this.slots.size(); ++index) {
ItemStack itemstack = this.slots.get(index).getItem();
this.triggerSlotListeners(index, itemstack, itemstack::copy);
}
for (int index = 0; index < this.dataSlots.size(); ++index) {
DataSlot containerproperty = this.dataSlots.get(index);
if (containerproperty.checkAndClearUpdateFlag()) {
this.updateDataSlotListeners(index, containerproperty.get());
}
}
this.sendAllDataToRemote();
}
private void updateDataSlotListeners(int i, int j) {
for (ContainerListener containerListener : this.containerListeners) {
containerListener.dataChanged(this, i, j);
}
}
private void triggerSlotListeners(int index, ItemStack itemStack, Supplier<ItemStack> supplier) {
ItemStack itemStack1 = this.lastSlots.get(index);
if (!ItemStack.matches(itemStack1, itemStack)) {
ItemStack itemStack2 = supplier.get();
this.lastSlots.set(index, itemStack2);
for (ContainerListener containerListener : this.containerListeners) {
containerListener.slotChanged(this, index, itemStack2);
}
}
}
private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier<ItemStack> supplier) {
if (!this.suppressRemoteUpdates) {
RemoteSlot slot = this.remoteSlots.get(i);
if (!slot.matches(itemStack)) {
slot.force(itemStack);
if (this.synchronizer != null) {
this.synchronizer.sendSlotChange(this, i, supplier.get());
}
}
}
}
private void synchronizeDataSlotToRemote(int index, int value) {
if (!this.suppressRemoteUpdates) {
int existing = this.remoteDataSlots.getInt(index);
if (existing != value) {
this.remoteDataSlots.set(index, value);
if (this.synchronizer != null) {
this.synchronizer.sendDataChange(this, index, value);
}
}
}
}
private void synchronizeCarriedToRemote() {
if (!this.suppressRemoteUpdates) {
ItemStack carried = this.getCarried();
if (!this.remoteCarried.matches(carried)) {
this.remoteCarried.force(carried);
if (this.synchronizer != null) {
this.synchronizer.sendCarriedChange(this, carried.copy());
}
}
}
}
@Override
public void setRemoteCarried(@NotNull HashedStack stack) {
this.remoteCarried.receive(stack);
}
@Override
public void suppressRemoteUpdates() {
this.suppressRemoteUpdates = true;
}
@Override
public void resumeRemoteUpdates() {
this.suppressRemoteUpdates = false;
}
}
@@ -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;
}
@@ -0,0 +1,262 @@
package com.lishid.openinv.internal.common.container.menu;
import com.google.common.base.Preconditions;
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;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class OpenInventoryMenu extends OpenChestMenu<OpenInventory> {
private int offset;
public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) {
super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly);
}
private static MenuType<ChestMenu> getMenuType(OpenInventory inventory, ServerPlayer viewer) {
int size = inventory.getContainerSize();
// Disallow duplicate access to own main inventory contents.
if (inventory.getOwnerHandle().equals(viewer)) {
size -= viewer.getInventory().getNonEquipmentItems().size();
size = ((int) Math.ceil(size / 9.0)) * 9;
}
return getChestMenuType(size);
}
@Override
protected void preSlotSetup() {
offset = ownContainer ? viewer.getInventory().getNonEquipmentItems().size() : 0;
}
@Override
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
index += offset;
Slot slot = container.getMenuSlot(index, x, y);
// If the slot cannot be interacted with there's nothing to configure.
if (slot.getClass().equals(SlotViewOnly.class)) {
return slot;
}
// Remove drop slot if viewer is not allowed to use it.
if (slot instanceof ContentDrop.SlotDrop
&& (viewOnly || !Permissions.INVENTORY_SLOT_DROP.hasPermission(viewer.getBukkitEntity()))) {
return new SlotViewOnly(container, index, x, y);
}
if (slot instanceof ContentEquipment.SlotEquipment equipment) {
if (viewOnly) {
return SlotViewOnly.wrap(slot);
}
Permissions perm = switch (equipment.getEquipmentSlot()) {
case HEAD -> Permissions.INVENTORY_SLOT_HEAD_ANY;
case CHEST -> Permissions.INVENTORY_SLOT_CHEST_ANY;
case LEGS -> Permissions.INVENTORY_SLOT_LEGS_ANY;
case FEET -> Permissions.INVENTORY_SLOT_FEET_ANY;
// Off-hand can hold anything, not just equipment.
default -> null;
};
// If the viewer doesn't have permission, only allow equipment the viewee can equip in the slot.
if (perm != null && !perm.hasPermission(viewer.getBukkitEntity())) {
equipment.onlyEquipmentFor(container.getOwnerHandle());
}
// Equipment slots are a core part of the inventory, so they will always be shown.
return slot;
}
// When viewing own inventory, only allow access to equipment and drop slots (equipment allowed above).
if (ownContainer && !(slot instanceof ContentDrop.SlotDrop)) {
return new SlotViewOnly(container, index, x, y);
}
if (viewOnly) {
return SlotViewOnly.wrap(slot);
}
return slot;
}
@Override
protected @NotNull CraftInventoryView<OpenChestMenu<OpenInventory>, Inventory> createBukkitEntity() {
org.bukkit.inventory.Inventory bukkitInventory;
if (viewOnly) {
bukkitInventory = new OpenDummyPlayerInventory(container);
} else if (ownContainer) {
bukkitInventory = new OpenPlayerInventorySelf(container, offset);
} else {
bukkitInventory = container.getBukkitInventory();
}
return new CraftInventoryView<>(viewer.getBukkitEntity(), bukkitInventory, this) {
@Override
public org.bukkit.inventory.ItemStack getItem(int index) {
if (viewOnly || index < 0) {
return null;
}
Slot slot = slots.get(index);
return CraftItemStack.asCraftMirror(slot.hasItem() ? slot.getItem() : ItemStack.EMPTY);
}
@Override
public boolean isInTop(int rawSlot) {
return rawSlot < topSize;
}
@Override
public @Nullable Inventory getInventory(int rawSlot) {
if (viewOnly) {
return null;
}
if (rawSlot == InventoryView.OUTSIDE || rawSlot == -1) {
return null;
}
Preconditions.checkArgument(rawSlot >= 0 && rawSlot < topSize + offset + BOTTOM_INVENTORY_SIZE,
"Slot %s outside of inventory", rawSlot);
if (rawSlot > topSize) {
return getBottomInventory();
}
Slot slot = slots.get(rawSlot);
if (slot.isFake()) {
return null;
}
return getTopInventory();
}
@Override
public int convertSlot(int rawSlot) {
if (viewOnly) {
return InventoryView.OUTSIDE;
}
if (rawSlot < 0) {
return rawSlot;
}
if (rawSlot < topSize) {
Slot slot = slots.get(rawSlot);
if (slot.isFake()) {
return InventoryView.OUTSIDE;
}
return rawSlot;
}
int slot = rawSlot - topSize;
if (slot >= 27) {
slot -= 27;
} else {
slot += 9;
}
return slot;
}
@Override
public @NotNull InventoryType.SlotType getSlotType(int slot) {
if (viewOnly || slot < 0) {
return InventoryType.SlotType.OUTSIDE;
}
if (slot >= topSize) {
slot -= topSize;
if (slot >= 27) {
return InventoryType.SlotType.QUICKBAR;
}
return InventoryType.SlotType.CONTAINER;
}
return OpenInventoryMenu.this.container.getSlotType(offset + slot);
}
@Override
public int countSlots() {
return topSize + BOTTOM_INVENTORY_SIZE;
}
};
}
@Override
public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) {
if (viewOnly) {
return ItemStack.EMPTY;
}
// See ChestMenu and InventoryMenu
Slot slot = this.slots.get(index);
if (!slot.hasItem() || slot.isFake()) {
return ItemStack.EMPTY;
}
ItemStack itemStack = slot.getItem();
ItemStack originalStack = itemStack.copy();
if (index < topSize) {
// If we're moving top to bottom, do a normal transfer.
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
return ItemStack.EMPTY;
}
} else {
EquipmentSlot equipmentSlot = player.getEquipmentSlotForItem(itemStack);
boolean movedGear = switch (equipmentSlot) {
// If this is gear, try to move it to the correct slot first.
case OFFHAND, FEET, LEGS, CHEST, HEAD -> {
// Locate the correct slot in the contents following the main inventory.
for (int extra = container.getOwnerHandle().getInventory().getNonEquipmentItems().size() - offset; extra < topSize; ++extra) {
Slot extraSlot = getSlot(extra);
if (extraSlot instanceof ContentEquipment.SlotEquipment equipSlot
&& equipSlot.getEquipmentSlot() == equipmentSlot) {
// If we've found a matching slot, try to move to it.
// If this succeeds, even partially, we will not attempt to move to other slots.
// Otherwise, armor is already occupied, so we'll fall through to main inventory.
yield this.moveItemStackTo(itemStack, extra, extra + 1, false);
}
}
yield false;
}
// Non-gear gets no special treatment.
default -> false;
};
// If main inventory is not available, there's nowhere else to move.
if (offset != 0) {
if (!movedGear) {
return ItemStack.EMPTY;
}
} else {
// If we didn't move to a gear slot, try to move to a main inventory slot.
if (!movedGear && !this.moveItemStackTo(itemStack, 0, container.getOwnerHandle().getInventory().getNonEquipmentItems().size(), true)) {
return ItemStack.EMPTY;
}
}
}
if (itemStack.isEmpty()) {
slot.setByPlayer(ItemStack.EMPTY);
} else {
slot.setChanged();
}
return originalStack;
}
}
@@ -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
@@ -0,0 +1,110 @@
package com.lishid.openinv.internal.common.container.slot;
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.ContainerHelper;
import net.minecraft.world.entity.EntityEquipment;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.jetbrains.annotations.NotNull;
/**
* A slot for equipment that displays placeholders if empty.
*/
public class ContentEquipment implements Content {
private EntityEquipment equipment;
private final ItemStack placeholder;
private final EquipmentSlot equipmentSlot;
public ContentEquipment(ServerPlayer holder, EquipmentSlot equipmentSlot) {
setHolder(holder);
placeholder = switch (equipmentSlot) {
case HEAD -> Placeholders.emptyHelmet;
case CHEST -> Placeholders.emptyChestplate;
case LEGS -> Placeholders.emptyLeggings;
case FEET -> Placeholders.emptyBoots;
default -> Placeholders.emptyOffHand;
};
this.equipmentSlot = equipmentSlot;
}
@Override
public void setHolder(@NotNull ServerPlayer holder) {
this.equipment = holder.getInventory().equipment;
}
@Override
public ItemStack get() {
return equipment.get(equipmentSlot);
}
@Override
public ItemStack remove() {
return equipment.set(equipmentSlot, ItemStack.EMPTY);
}
@Override
public ItemStack removePartial(int amount) {
ItemStack current = get();
if (!current.isEmpty() && amount > 0) {
return current.split(amount);
}
return ItemStack.EMPTY;
}
@Override
public void set(ItemStack itemStack) {
equipment.set(equipmentSlot, itemStack);
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotEquipment(container, slot, x, y);
}
@Override
public InventoryType.SlotType getSlotType() {
return InventoryType.SlotType.ARMOR;
}
public class SlotEquipment extends SlotPlaceholder {
private ServerPlayer viewer;
SlotEquipment(Container container, int index, int x, int y) {
super(container, index, x, y);
}
@Override
public ItemStack getOrDefault() {
ItemStack itemStack = getItem();
if (!itemStack.isEmpty()) {
return itemStack;
}
return placeholder;
}
public EquipmentSlot getEquipmentSlot() {
return equipmentSlot;
}
public void onlyEquipmentFor(ServerPlayer viewer) {
this.viewer = viewer;
}
@Override
public boolean mayPlace(@NotNull ItemStack itemStack) {
if (viewer == null) {
return true;
}
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;
@@ -0,0 +1,52 @@
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;
/**
* A slot for equipment that updates held items if necessary.
*/
public class ContentOffHand extends ContentEquipment {
private ServerPlayer holder;
public ContentOffHand(ServerPlayer holder) {
super(holder, EquipmentSlot.OFFHAND);
}
@Override
public void setHolder(@NotNull ServerPlayer holder) {
super.setHolder(holder);
this.holder = holder;
}
@Override
public InventoryType.SlotType getSlotType() {
return InventoryType.SlotType.QUICKBAR;
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotEquipment(container, slot, x, y) {
@Override
public void setChanged() {
if (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,40 @@
package com.lishid.openinv.internal.common.container.slot.placeholder;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.TagParser;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomModelData;
import net.minecraft.world.item.component.DyedItemColor;
import net.minecraft.world.item.component.TooltipDisplay;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedHashSet;
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());
private static final TooltipDisplay HIDE_TOOLTIP = new TooltipDisplay(true, new LinkedHashSet<>());
@Override
protected @NotNull CompoundTag parseTag(@NotNull String itemText) throws Exception {
return TagParser.parseCompoundFully(itemText);
}
@Override
protected void addModelData(@NotNull ItemStack itemStack) {
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
}
@Override
protected void hideTooltip(@NotNull ItemStack itemStack) {
itemStack.set(DataComponents.TOOLTIP_DISPLAY, HIDE_TOOLTIP);
}
@Override
protected DyedItemColor getDye(int rgb) {
return new DyedItemColor(rgb);
}
}
@@ -1,18 +1,14 @@
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;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.TagParser;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Unit;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.CustomModelData;
import net.minecraft.world.item.component.DyedItemColor;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.ItemLike;
@@ -20,132 +16,134 @@ 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,
private @NotNull ItemStack parse(
@Nullable ConfigurationSection section,
@NotNull String path,
@NotNull ItemStack defaultStack) throws Exception {
if (section == null) {
return defaultStack;
}
String itemText = section.getString(path);
if (itemText == null) {
return defaultStack;
}
CompoundTag compoundTag = TagParser.parseTag(itemText);
CompoundTag compoundTag = parseTag(itemText);
Optional<ItemStack> parsed = ItemStack.parse(CraftRegistry.getMinecraftRegistry(), compoundTag);
return parsed.filter(itemStack -> !itemStack.isEmpty()).orElse(defaultStack);
}
public static ItemStack survivalOnly(@NotNull ServerPlayer serverPlayer) {
if (serverPlayer.connection == null || serverPlayer.connection.isDisconnected()) {
return blockedOffline;
}
protected abstract @NotNull CompoundTag parseTag(@NotNull String itemText) throws Exception;
return BLOCKED_GAME_TYPE.getOrDefault(serverPlayer.gameMode.getGameModeForPlayer(), ItemStack.EMPTY);
}
protected abstract void addModelData(@NotNull ItemStack itemStack);
private static ItemStack defaultCraftingOutput() {
protected abstract void hideTooltip(@NotNull ItemStack itemStack);
protected abstract DyedItemColor getDye(int rgb);
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);
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
addModelData(itemStack);
hideTooltip(itemStack);
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);
DyedItemColor color = getDye(0xC8C8C8);
itemStack.set(DataComponents.DYED_COLOR, color);
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
hideTooltip(itemStack);
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),
@@ -153,20 +151,20 @@ public final class Placeholders {
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA),
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK),
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK))));
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
hideTooltip(itemStack);
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);
hideTooltip(itemStack);
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 +174,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,12 +8,14 @@ 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;
import org.jetbrains.annotations.Unmodifiable;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -29,7 +31,8 @@ public class OpenPlayer extends CraftPlayer {
* @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(
@Unmodifiable
protected static final Set<String> RESET_TAGS = Set.of(
// Entity#saveWithoutId(CompoundTag)
"CustomName",
"CustomNameVisible",
@@ -42,6 +45,7 @@ public class OpenPlayer extends CraftPlayer {
"Passengers",
// ServerPlayer#addAdditionalSaveData(CompoundTag)
// Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle
// As of 1.21.5 only ender_pearls tag still needs to be reset. Rest are for backwards compatibility.
"warden_spawn_tracker",
"enteredNetherPosition",
"SpawnX",
@@ -51,22 +55,28 @@ public class OpenPlayer extends CraftPlayer {
"SpawnAngle",
"SpawnDimension",
"raid_omen_position",
"ender_pearls",
// Player#addAdditionalSaveData(CompoundTag)
"ShoulderEntityLeft",
"ShoulderEntityRight",
"LastDeathLocation",
"current_explosion_impact_pos",
"current_explosion_impact_pos", // Unnecessary in 1.21.5
// LivingEntity#addAdditionalSaveData(CompoundTag)
"active_effects",
"SleepingX",
"SleepingY",
"SleepingZ",
"Brain"
"Brain",
"last_hurt_by_player",
"last_hurt_by_player_memory_time",
"last_hurt_by_mob",
"ticks_since_last_hurt_by_mob",
"equipment"
);
private final PlayerManager manager;
OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) {
protected OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) {
super(server, entity);
this.manager = manager;
}
@@ -109,7 +119,7 @@ public class OpenPlayer extends CraftPlayer {
}
@Contract("null -> new")
private @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) {
protected @NotNull CompoundTag getWritableTag(@Nullable CompoundTag oldData) {
if (oldData == null) {
return new CompoundTag();
}
@@ -118,8 +128,7 @@ public class OpenPlayer extends CraftPlayer {
oldData = oldData.copy();
// Remove vanilla/server data that is not written every time.
oldData.getAllKeys()
.removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit"));
oldData.keySet().removeIf(key -> RESET_TAGS.contains(key) || key.startsWith("Bukkit") || key.startsWith("Paper") && key.length() > 5);
return oldData;
}
@@ -174,4 +183,8 @@ public class OpenPlayer extends CraftPlayer {
}
}
public static boolean isConnected(@Nullable ServerGamePacketListenerImpl connection) {
return connection != null && !connection.isDisconnected();
}
}
@@ -0,0 +1,278 @@
package com.lishid.openinv.internal.common.player;
import com.lishid.openinv.internal.ISpecialInventory;
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;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.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;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.Optional;
import java.util.UUID;
import java.util.logging.Logger;
public class PlayerManager implements com.lishid.openinv.internal.PlayerManager {
protected final @NotNull Logger logger;
protected @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;
}
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).
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);
// World is not loaded by ServerPlayer#load(CompoundTag) on Paper.
parseWorld(player, loadedData);
return true;
}
protected void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) {
// See PlayerList#placeNewPlayer
World bukkitWorld;
Optional<Long> msbs = loadedData.getLong("WorldUUIDMost");
Optional<Long> lsbs = loadedData.getLong("WorldUUIDLeast");
if (msbs.isPresent() && lsbs.isPresent()) {
// Modern Bukkit world.
bukkitWorld = Bukkit.getServer().getWorld(new UUID(msbs.get(), lsbs.get()));
} else {
Optional<String> worldName = loadedData.getString("world");
if (worldName.isPresent()) {
// Legacy Bukkit world.
bukkitWorld = Bukkit.getServer().getWorld(worldName.get());
} 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, () -> spawnInDefaultWorld(player));
return;
}
}
if (bukkitWorld == null) {
spawnInDefaultWorld(player);
return;
}
player.setServerLevel(((CraftWorld) bukkitWorld).getHandle());
}
protected void spawnInDefaultWorld(ServerPlayer player) {
ServerLevel level = player.server.getLevel(Level.OVERWORLD);
if (level != null) {
// Adjust player to default spawn (in keeping with Paper handling) when world not found.
player.snapTo(player.adjustSpawnLocation(level, level.getSharedSpawnPos()).getBottomCenter(), level.getSharedSpawnAngle(), 0.0F);
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;
}
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);
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(), title));
player.initMenu(menu);
return menu.getBukkitView();
}
}
+28
View File
@@ -0,0 +1,28 @@
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_4"))
implementation(project(":openinvadapterpaper1_21_3"))
paperweight.paperDevBundle("1.21.1-R0.1-SNAPSHOT")
}
@@ -1,29 +1,37 @@
package com.lishid.openinv.internal.v1_20_R4;
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.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.internal.paper1_21_4.container.OpenEnderChest;
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.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.paper1_21_4.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,55 @@
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.paper1_21_4.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;
}
}
+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_4"))
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.paper1_21_4.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,12 @@
package com.lishid.openinv.internal.paper1_21_3.container.slot.placeholder;
import com.lishid.openinv.internal.paper1_21_4.container.slot.placeholder.CustomModelBase;
import net.minecraft.world.item.component.CustomModelData;
public class NumericDataPlaceholderLoader extends CustomModelBase {
public NumericDataPlaceholderLoader() {
super(new CustomModelData(9999));
}
}
+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.4-R0.1-SNAPSHOT")
}
@@ -1,28 +1,36 @@
package com.lishid.openinv.internal.v1_20_R3;
package com.lishid.openinv.internal.paper1_21_4;
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.paper1_21_4.container.OpenEnderChest;
import com.lishid.openinv.internal.paper1_21_4.container.OpenInventory;
import com.lishid.openinv.internal.paper1_21_4.container.slot.placeholder.CustomModelPlaceholderLoader;
import com.lishid.openinv.internal.paper1_21_4.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 {
protected 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);
this.logger = logger;
manager = new PlayerManager(logger);
anySilentContainer = new AnySilentContainer(logger, lang);
}
@@ -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 CustomModelPlaceholderLoader().load(placeholders);
} catch (Exception e) {
logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e);
}
}
}
@@ -0,0 +1,23 @@
package com.lishid.openinv.internal.paper1_21_4.container;
import com.lishid.openinv.internal.paper1_21_4.container.menu.OpenEnderChestMenu;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class OpenEnderChest extends com.lishid.openinv.internal.common.container.OpenEnderChest{
public OpenEnderChest(@NotNull Player player) {
super(player);
}
public @Nullable AbstractContainerMenu createMenu(net.minecraft.world.entity.player.Player player, int i, boolean viewOnly) {
if (player instanceof ServerPlayer serverPlayer) {
return new OpenEnderChestMenu(this, serverPlayer, i, viewOnly);
}
return null;
}
}
@@ -0,0 +1,177 @@
package com.lishid.openinv.internal.paper1_21_4.container;
import com.lishid.openinv.internal.common.container.slot.ContentCrafting;
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.ContentList;
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.paper1_21_4.container.menu.OpenInventoryMenu;
import com.lishid.openinv.internal.paper1_21_4.container.slot.ContentEquipment;
import com.lishid.openinv.internal.paper1_21_4.container.slot.ContentOffHand;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class OpenInventory extends com.lishid.openinv.internal.common.container.OpenInventory {
public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) {
super(bukkitPlayer);
}
@Override
protected void setupSlots() {
// Top of inventory: Regular contents.
int nextIndex = addMainInventory();
// If inventory is expected size, we can arrange slots to be pretty.
Inventory ownerInv = owner.getInventory();
if (ownerInv.items.size() == 36
&& ownerInv.armor.size() == 4
&& ownerInv.offhand.size() == 1
&& owner.inventoryMenu.getCraftSlots().getContainerSize() == 4) {
// Armor slots: Bottom left.
addArmor(36);
// Off-hand: Below chestplate.
addOffHand(46);
// Drop slot: Bottom right.
slots.set(53, new ContentDrop(owner));
// Cursor slot: Above drop.
slots.set(44, new ContentCursor(owner));
// Crafting is displayed in the bottom right corner.
// As we're using the pretty view, this is a 3x2.
addCrafting(41, true);
return;
}
// Otherwise we'll just add elements linearly.
nextIndex = addArmor(nextIndex);
nextIndex = addOffHand(nextIndex);
nextIndex = addCrafting(nextIndex, false);
slots.set(nextIndex, new ContentCursor(owner));
// Drop slot last.
slots.set(slots.size() - 1, new ContentDrop(owner));
}
private int addMainInventory() {
int listSize = owner.getInventory().items.size();
// Hotbar slots are 0-8. We want those to appear on the bottom of the inventory like a normal player inventory,
// so everything else needs to move up a row.
int hotbarDiff = listSize - 9;
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
InventoryType.SlotType type;
int invIndex;
if (localIndex < hotbarDiff) {
invIndex = localIndex + 9;
type = InventoryType.SlotType.CONTAINER;
} else {
type = InventoryType.SlotType.QUICKBAR;
invIndex = localIndex - hotbarDiff;
}
slots.set(localIndex, new ContentList(owner, invIndex, type) {
@Override
public void setHolder(@NotNull ServerPlayer holder) {
items = holder.getInventory().items;
}
});
}
return listSize;
}
private int addArmor(int startIndex) {
int listSize = owner.getInventory().armor.size();
for (int i = 0; i < listSize; ++i) {
// Armor slots go bottom to top; boots are slot 0, helmet is slot 3.
// Since we have to display horizontally due to space restrictions,
// making the left side the "top" is more user-friendly.
int armorIndex;
EquipmentSlot slot;
switch (i) {
case 3 -> {
armorIndex = 0;
slot = EquipmentSlot.FEET;
}
case 2 -> {
armorIndex = 1;
slot = EquipmentSlot.LEGS;
}
case 1 -> {
armorIndex = 2;
slot = EquipmentSlot.CHEST;
}
case 0 -> {
armorIndex = 3;
slot = EquipmentSlot.HEAD;
}
default -> {
// In the event that new armor slots are added, they can be placed at the end.
armorIndex = i;
slot = EquipmentSlot.MAINHAND;
}
}
slots.set(startIndex + i, new ContentEquipment(owner, armorIndex, slot));
}
return startIndex + listSize;
}
private int addOffHand(int startIndex) {
int listSize = owner.getInventory().offhand.size();
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
slots.set(startIndex + localIndex, new ContentOffHand(owner, localIndex));
}
return startIndex + listSize;
}
private int addCrafting(int startIndex, boolean pretty) {
int listSize = owner.inventoryMenu.getCraftSlots().getContents().size();
pretty &= listSize == 4;
for (int localIndex = 0; localIndex < listSize; ++localIndex) {
// Pretty display is a 2x2 rather than linear.
// If index is in top row, grid is not 2x2, or pretty is disabled, just use current index.
// Otherwise, subtract 2 and add 9 to start in the same position on the next row.
int modIndex = startIndex + (localIndex < 2 || !pretty ? localIndex : localIndex + 7);
slots.set(modIndex, new ContentCrafting(owner, localIndex));
}
if (pretty) {
slots.set(startIndex + 2, new ContentViewOnly(owner) {
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotViewOnly(container, slot, x, y) {
@Override
public ItemStack getOrDefault() {
return Placeholders.craftingOutput;
}
};
}
});
slots.set(startIndex + 11, getCraftingResult(owner));
}
return startIndex + listSize;
}
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
if (player instanceof ServerPlayer serverPlayer) {
return new OpenInventoryMenu(this, serverPlayer, i, viewOnly);
}
return null;
}
}
@@ -1,12 +1,12 @@
package com.lishid.openinv.internal.v1_21_R1.container.bukkit;
package com.lishid.openinv.internal.paper1_21_4.container.bukkit;
import com.google.common.base.Preconditions;
import com.lishid.openinv.internal.v1_21_R1.container.OpenInventory;
import com.lishid.openinv.internal.paper1_21_4.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;
@@ -26,7 +26,7 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInvento
}
@Override
public ItemStack[] getContents() {
public ItemStack @NotNull [] getContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents());
}
@@ -46,7 +46,7 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInvento
}
@Override
public ItemStack[] getStorageContents() {
public ItemStack @NotNull [] getStorageContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().items);
}
@@ -71,12 +71,12 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInvento
}
@Override
public @NotNull ItemStack[] getArmorContents() {
public @NotNull ItemStack @NotNull [] getArmorContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().armor);
}
@Override
public void setArmorContents(@Nullable ItemStack[] items) {
public void setArmorContents(ItemStack @NotNull [] items) {
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().armor;
int size = list.size();
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
@@ -86,12 +86,12 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInvento
}
@Override
public @NotNull ItemStack[] getExtraContents() {
public @NotNull ItemStack @NotNull [] getExtraContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().offhand);
}
@Override
public void setExtraContents(@Nullable ItemStack[] items) {
public void setExtraContents(ItemStack @NotNull [] items) {
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().offhand;
int size = list.size();
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
@@ -101,7 +101,7 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInvento
}
@Override
public @Nullable ItemStack getHelmet() {
public @NotNull ItemStack getHelmet() {
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
.getArmor(EquipmentSlot.HEAD.getIndex()));
}
@@ -113,9 +113,9 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInvento
}
@Override
public @Nullable ItemStack getChestplate() {
public @NotNull ItemStack getChestplate() {
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
.getArmor(EquipmentSlot.HEAD.getIndex()));
.getArmor(EquipmentSlot.CHEST.getIndex()));
}
@Override
@@ -125,7 +125,7 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInvento
}
@Override
public @Nullable ItemStack getLeggings() {
public @NotNull ItemStack getLeggings() {
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
.getArmor(EquipmentSlot.LEGS.getIndex()));
}
@@ -137,7 +137,7 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInvento
}
@Override
public @Nullable ItemStack getBoots() {
public @NotNull ItemStack getBoots() {
return CraftItemStack.asCraftMirror(getInventory().getOwnerHandle().getInventory()
.getArmor(EquipmentSlot.FEET.getIndex()));
}
@@ -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);
@@ -193,7 +195,7 @@ public class OpenPlayerInventory extends CraftInventory implements PlayerInvento
}
@Override
public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) {
public @NotNull ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) {
return switch (slot) {
case HAND -> getItemInMainHand();
case OFF_HAND -> getItemInOffHand();
@@ -0,0 +1,26 @@
package com.lishid.openinv.internal.paper1_21_4.container.bukkit;
import com.lishid.openinv.internal.paper1_21_4.container.OpenInventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
public class OpenPlayerInventorySelf extends OpenPlayerInventory {
private final int offset;
public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) {
super(inventory);
this.offset = offset;
}
@Override
public ItemStack getItem(int index) {
return super.getItem(offset + index);
}
@Override
public void setItem(int index, ItemStack item) {
super.setItem(offset + index, item);
}
}
@@ -1,11 +1,11 @@
package com.lishid.openinv.internal.v1_21_R1.container.menu;
package com.lishid.openinv.internal.paper1_21_4.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<>();
@@ -108,31 +108,19 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
}
}
public static @NotNull MenuType<ChestMenu> getChestMenuType(int inventorySize) {
inventorySize = ((int) Math.ceil(inventorySize / 9.0)) * 9;
return switch (inventorySize) {
case 9 -> MenuType.GENERIC_9x1;
case 18 -> MenuType.GENERIC_9x2;
case 27 -> MenuType.GENERIC_9x3;
case 36 -> MenuType.GENERIC_9x4;
case 45 -> MenuType.GENERIC_9x5;
case 54 -> MenuType.GENERIC_9x6;
default -> throw new IllegalArgumentException("Inventory size unsupported: " + inventorySize);
};
}
protected void preSlotSetup() {}
protected @NotNull Slot getUpperSlot(int index, int x, int y) {
Slot slot = new Slot(container, index, x, y);
if (viewOnly) {
return 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 +128,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 +164,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 +270,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 +281,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 +296,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 +310,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 +318,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 +350,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);
}
@@ -0,0 +1,59 @@
package com.lishid.openinv.internal.paper1_21_4.container.menu;
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;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
public class OpenEnderChestMenu extends OpenChestMenu<OpenEnderChest> {
public OpenEnderChestMenu(
@NotNull OpenEnderChest enderChest,
@NotNull ServerPlayer viewer,
int containerId,
boolean viewOnly) {
super(
com.lishid.openinv.internal.common.container.menu.OpenChestMenu.getChestMenuType(enderChest.getContainerSize()),
containerId,
enderChest,
viewer,
viewOnly
);
}
@Override
public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) {
if (viewOnly) {
return ItemStack.EMPTY;
}
// See ChestMenu
Slot slot = this.slots.get(index);
if (slot.isFake() || !slot.hasItem()) {
return ItemStack.EMPTY;
}
ItemStack itemStack = slot.getItem();
ItemStack original = itemStack.copy();
if (index < topSize) {
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
return ItemStack.EMPTY;
}
} else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) {
return ItemStack.EMPTY;
}
if (itemStack.isEmpty()) {
slot.setByPlayer(ItemStack.EMPTY);
} else {
slot.setChanged();
}
return original;
}
}
@@ -1,12 +1,12 @@
package com.lishid.openinv.internal.v1_21_R1.container.menu;
package com.lishid.openinv.internal.paper1_21_4.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.bukkit.OpenDummyPlayerInventory;
import com.lishid.openinv.internal.common.container.slot.ContentDrop;
import com.lishid.openinv.internal.common.container.slot.SlotViewOnly;
import com.lishid.openinv.internal.paper1_21_4.container.OpenInventory;
import com.lishid.openinv.internal.paper1_21_4.container.bukkit.OpenPlayerInventorySelf;
import com.lishid.openinv.internal.paper1_21_4.container.slot.ContentEquipment;
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;
@@ -39,7 +39,7 @@ public class OpenInventoryMenu extends OpenChestMenu<OpenInventory> {
size = ((int) Math.ceil(size / 9.0)) * 9;
}
return getChestMenuType(size);
return com.lishid.openinv.internal.common.container.menu.OpenChestMenu.getChestMenuType(size);
}
@Override
@@ -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,6 +1,8 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.paper1_21_4.container.slot;
import com.lishid.openinv.internal.v1_21_R1.container.Placeholders;
import com.lishid.openinv.internal.common.container.slot.ContentList;
import com.lishid.openinv.internal.common.container.slot.SlotPlaceholder;
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 +67,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,8 +1,11 @@
package com.lishid.openinv.internal.v1_21_R1.container.slot;
package com.lishid.openinv.internal.paper1_21_4.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()));
}
}
};
@@ -0,0 +1,41 @@
package com.lishid.openinv.internal.paper1_21_4.container.slot.placeholder;
import com.lishid.openinv.internal.common.container.slot.placeholder.PlaceholderLoaderBase;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.TagParser;
import net.minecraft.util.Unit;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomModelData;
import net.minecraft.world.item.component.DyedItemColor;
import org.jetbrains.annotations.NotNull;
public class CustomModelBase extends PlaceholderLoaderBase {
private final @NotNull CustomModelData defaultCustomModelData;
public CustomModelBase(@NotNull CustomModelData defaultCustomModelData) {
this.defaultCustomModelData = defaultCustomModelData;
}
@Override
protected @NotNull CompoundTag parseTag(@NotNull String itemText) throws Exception {
return TagParser.parseTag(itemText);
}
@Override
protected void addModelData(@NotNull ItemStack itemStack) {
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, defaultCustomModelData);
}
@Override
protected void hideTooltip(@NotNull ItemStack itemStack) {
itemStack.set(DataComponents.HIDE_TOOLTIP, Unit.INSTANCE);
}
@Override
protected DyedItemColor getDye(int rgb) {
return new DyedItemColor(rgb, false);
}
}
@@ -0,0 +1,13 @@
package com.lishid.openinv.internal.paper1_21_4.container.slot.placeholder;
import net.minecraft.world.item.component.CustomModelData;
import java.util.List;
public class CustomModelPlaceholderLoader extends CustomModelBase {
public CustomModelPlaceholderLoader() {
super(new CustomModelData(List.of(), List.of(), List.of("openinv:custom"), List.of()));
}
}
@@ -0,0 +1,35 @@
package com.lishid.openinv.internal.paper1_21_4.player;
import com.lishid.openinv.internal.common.player.PlayerManager;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import org.bukkit.craftbukkit.CraftServer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class OpenPlayer extends com.lishid.openinv.internal.common.player.OpenPlayer {
protected OpenPlayer(CraftServer server, ServerPlayer entity,
PlayerManager manager) {
super(server, entity, manager);
}
@Contract("null -> new")
@Override
protected @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;
}
}
@@ -0,0 +1,70 @@
package com.lishid.openinv.internal.paper1_21_4.player;
import com.mojang.serialization.Dynamic;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
import java.util.logging.Logger;
public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager {
public PlayerManager(@NotNull Logger logger) {
super(logger);
}
protected 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, () -> spawnInDefaultWorld(player));
return;
}
if (bukkitWorld == null) {
spawnInDefaultWorld(player);
return;
}
player.setServerLevel(((CraftWorld) bukkitWorld).getHandle());
}
@Override
protected void spawnInDefaultWorld(ServerPlayer player) {
ServerLevel level = player.server.getLevel(Level.OVERWORLD);
if (level != null) {
// Adjust player to default spawn (in keeping with Paper handling) when world not found.
player.moveTo(player.adjustSpawnLocation(level, level.getSharedSpawnPos()).getBottomCenter(), level.getSharedSpawnAngle(), 0.0F);
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;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new OpenPlayer(player.server.server, player, this));
}
}
+44
View File
@@ -0,0 +1,44 @@
import com.github.jikoo.openinv.SpigotDependencyExtension
import com.github.jikoo.openinv.SpigotReobf
import com.github.jikoo.openinv.SpigotSetup
plugins {
`openinv-base`
alias(libs.plugins.shadow)
}
apply<SpigotSetup>()
apply<SpigotReobf>()
val spigotVer = "1.21.5-R0.1-SNAPSHOT"
// Used by common adapter to relocate Craftbukkit classes to a versioned package.
rootProject.extra["craftbukkitPackage"] = "v1_21_R4"
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")
}
}
dependencies {
compileOnly(libs.spigotapi)
extensions.getByType(SpigotDependencyExtension::class.java).version = spigotVer
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")
}
@@ -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("gameModeForPlayer");
this.serverPlayerGameModeGameType.setAccessible(true);
} catch (NoSuchFieldException e) {
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.
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,188 @@
package com.lishid.openinv.internal.reobf.container.bukkit;
import com.google.common.base.Preconditions;
import com.lishid.openinv.internal.reobf.container.OpenInventory;
import net.minecraft.core.NonNullList;
import net.minecraft.world.entity.player.Inventory;
import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftInventory;
import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class OpenPlayerInventory extends CraftInventory implements PlayerInventory {
public OpenPlayerInventory(@NotNull OpenInventory inventory) {
super(inventory);
}
@Override
public @NotNull OpenInventory getInventory() {
return (OpenInventory) super.getInventory();
}
@Override
public ItemStack @NotNull [] getContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents());
}
@Override
public void setContents(ItemStack[] items) {
Inventory internal = getInventory().getOwnerHandle().getInventory();
int size = internal.getContainerSize();
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
for (int index = 0; index < size; ++index) {
if (index < items.length) {
internal.setItem(index, CraftItemStack.asNMSCopy(items[index]));
} else {
internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY);
}
}
}
@Override
public ItemStack @NotNull [] getStorageContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getNonEquipmentItems());
}
@Override
public void setStorageContents(ItemStack[] items) throws IllegalArgumentException {
NonNullList<net.minecraft.world.item.ItemStack> list = getInventory().getOwnerHandle().getInventory().getNonEquipmentItems();
int size = list.size();
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
for (int index = 0; index < items.length; ++index) {
list.set(index, CraftItemStack.asNMSCopy(items[index]));
}
}
@Override
public @NotNull InventoryType getType() {
return InventoryType.PLAYER;
}
@Override
public @NotNull Player getHolder() {
return getInventory().getOwner();
}
@Override
public @NotNull ItemStack @NotNull [] getArmorContents() {
return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getArmorContents();
}
@Override
public void setArmorContents(ItemStack @NotNull [] items) {
getInventory().getOwnerHandle().getBukkitEntity().getInventory().setArmorContents(items);
}
@Override
public @NotNull ItemStack @NotNull [] getExtraContents() {
return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getExtraContents();
}
@Override
public void setExtraContents(ItemStack @NotNull [] items) {
getInventory().getOwnerHandle().getBukkitEntity().getInventory().setExtraContents(items);
}
@Override
public @Nullable ItemStack getHelmet() {
return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getHelmet();
}
@Override
public void setHelmet(@Nullable ItemStack helmet) {
getInventory().getOwnerHandle().getBukkitEntity().getInventory().setHelmet(helmet);
}
@Override
public @Nullable ItemStack getChestplate() {
return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getChestplate();
}
@Override
public void setChestplate(@Nullable ItemStack chestplate) {
getInventory().getOwnerHandle().getBukkitEntity().getInventory().setChestplate(chestplate);
}
@Override
public @Nullable ItemStack getLeggings() {
return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getLeggings();
}
@Override
public void setLeggings(@Nullable ItemStack leggings) {
getInventory().getOwnerHandle().getBukkitEntity().getInventory().setLeggings(leggings);
}
@Override
public @Nullable ItemStack getBoots() {
return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getBoots();
}
@Override
public void setBoots(@Nullable ItemStack boots) {
getInventory().getOwnerHandle().getBukkitEntity().getInventory().setBoots(boots);
}
@Override
public @NotNull ItemStack getItemInMainHand() {
Inventory internal = getInventory().getOwnerHandle().getInventory();
return CraftItemStack.asCraftMirror(internal.getSelectedItem());
}
@Override
public void setItemInMainHand(@Nullable ItemStack item) {
Inventory internal = getInventory().getOwnerHandle().getInventory();
internal.setSelectedItem(CraftItemStack.asNMSCopy(item));
}
@Override
public @NotNull ItemStack getItemInOffHand() {
return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getItemInOffHand();
}
@Override
public void setItemInOffHand(@Nullable ItemStack item) {
getInventory().getOwnerHandle().getBukkitEntity().getInventory().setItemInOffHand(item);
}
@Deprecated
@Override
public @NotNull ItemStack getItemInHand() {
return getItemInMainHand();
}
@Deprecated
@Override
public void setItemInHand(@Nullable ItemStack stack) {
setItemInMainHand(stack);
}
@Override
public int getHeldItemSlot() {
Inventory internal = getInventory().getOwnerHandle().getInventory();
return internal.getNonEquipmentItems().size() - 9 + internal.getSelectedSlot();
}
@Override
public void setHeldItemSlot(int slot) {
slot %= 9;
getInventory().getOwnerHandle().getInventory().setSelectedSlot(slot);
}
@Override
public @Nullable ItemStack getItem(@NotNull org.bukkit.inventory.EquipmentSlot slot) {
return getInventory().getOwnerHandle().getBukkitEntity().getInventory().getItem(slot);
}
@Override
public void setItem(@NotNull org.bukkit.inventory.EquipmentSlot slot, @Nullable ItemStack item) {
getInventory().getOwnerHandle().getBukkitEntity().getInventory().setItem(slot, item);
}
}
@@ -0,0 +1,116 @@
package com.lishid.openinv.internal.reobf.container.slot;
import com.lishid.openinv.internal.reobf.container.slot.placeholder.Placeholders;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.v1_21_R4.CraftEquipmentSlot;
import org.bukkit.craftbukkit.v1_21_R4.inventory.CraftItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
public class ContentEquipment implements Content {
private PlayerInventory equipment;
private final ItemStack placeholder;
private final org.bukkit.inventory.EquipmentSlot equipmentSlot;
public ContentEquipment(ServerPlayer holder, EquipmentSlot equipmentSlot) {
setHolder(holder);
placeholder = switch (equipmentSlot) {
case HEAD -> Placeholders.emptyHelmet;
case CHEST -> Placeholders.emptyChestplate;
case LEGS -> Placeholders.emptyLeggings;
case FEET -> Placeholders.emptyBoots;
default -> Placeholders.emptyOffHand;
};
this.equipmentSlot = CraftEquipmentSlot.getSlot(equipmentSlot);
}
@Override
public void setHolder(@NotNull ServerPlayer holder) {
this.equipment = holder.getBukkitEntity().getInventory();
}
@Override
public ItemStack get() {
return CraftItemStack.asNMSCopy(equipment.getItem(equipmentSlot));
}
@Override
public ItemStack remove() {
org.bukkit.inventory.ItemStack old = equipment.getItem(equipmentSlot);
equipment.setItem(equipmentSlot, null);
return CraftItemStack.asNMSCopy(old);
}
@Override
public ItemStack removePartial(int amount) {
if (amount <= 0) {
return ItemStack.EMPTY;
}
ItemStack current = get();
if (current.isEmpty()) {
return ItemStack.EMPTY;
}
ItemStack split = current.split(amount);
set(current);
return split;
}
@Override
public void set(ItemStack itemStack) {
equipment.setItem(equipmentSlot, CraftItemStack.asCraftMirror(itemStack));
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotEquipment(container, slot, x, y);
}
@Override
public InventoryType.SlotType getSlotType() {
return InventoryType.SlotType.ARMOR;
}
public class SlotEquipment extends SlotPlaceholder {
private ServerPlayer viewer;
SlotEquipment(Container container, int index, int x, int y) {
super(container, index, x, y);
}
@Override
public ItemStack getOrDefault() {
ItemStack itemStack = getItem();
if (!itemStack.isEmpty()) {
return itemStack;
}
return placeholder;
}
public EquipmentSlot getEquipmentSlot() {
return CraftEquipmentSlot.getNMS(equipmentSlot);
}
public void onlyEquipmentFor(ServerPlayer viewer) {
this.viewer = viewer;
}
@Override
public boolean mayPlace(@NotNull ItemStack itemStack) {
if (viewer == null) {
return true;
}
return equipmentSlot == org.bukkit.inventory.EquipmentSlot.OFF_HAND
|| viewer.getEquipmentSlotForItem(itemStack) == CraftEquipmentSlot.getNMS(equipmentSlot);
}
}
}
@@ -0,0 +1,49 @@
package com.lishid.openinv.internal.reobf.container.slot;
import com.lishid.openinv.internal.reobf.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;
public class ContentOffHand extends ContentEquipment {
private ServerPlayer holder;
public ContentOffHand(ServerPlayer holder) {
super(holder, EquipmentSlot.OFFHAND);
}
@Override
public void setHolder(@NotNull ServerPlayer holder) {
super.setHolder(holder);
this.holder = holder;
}
@Override
public InventoryType.SlotType getSlotType() {
return InventoryType.SlotType.QUICKBAR;
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotEquipment(container, slot, x, y) {
@Override
public void setChanged() {
if (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,53 +1,37 @@
package com.lishid.openinv.internal.v1_21_R1.player;
package com.lishid.openinv.internal.reobf.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.reobf.container.OpenEnderChest;
import com.lishid.openinv.internal.reobf.container.OpenInventory;
import com.mojang.authlib.GameProfile;
import com.mojang.serialization.Dynamic;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ParticleStatus;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_21_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.v1_21_R4.CraftServer;
import org.bukkit.craftbukkit.v1_21_R4.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_R4.event.CraftEventFactory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.UUID;
import java.util.logging.Logger;
public class PlayerManager implements com.lishid.openinv.internal.PlayerManager {
private static boolean paper;
static {
try {
Class.forName("io.papermc.paper.configuration.Configuration");
paper = true;
} catch (ClassNotFoundException ignored) {
paper = false;
}
}
private final @NotNull Logger logger;
private @Nullable Field bukkitEntity;
@@ -123,7 +107,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,39 +141,9 @@ 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);
}
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;
@@ -221,7 +176,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;
}
-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.0</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.0</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);
}
}
}
Loaded 100 of 168 files, more files were not shown because too many files have changed in this diff. Show more