Compare commits

..
672 Commits
Author SHA1 Message Date
Jikoo 3820d2e974 Bump version to 5.3.4 for release 2026-09-23 08:00:57 -04:00
Adam 337ba2c35a Update to 26.3 (#430)
* Some overall code health improvement
* Spigot BlockState handling optimization
2026-09-23 07:58:00 -04:00
Adam fdf7f68c51 Improve player lookups with inconsistent casing (#428) 2026-09-13 17:21:02 +00:00
dependabot[bot] f1253fea6f Bump softprops/action-gh-release from 3.0.2 to 3.0.3 (#423)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 3.0.2 to 3.0.3.
- [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/v3.0.2...v3.0.3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.3
  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>
2026-09-01 17:18:25 -04:00
dependabot[bot] 5af49a6bbd Bump actions/setup-java from 5 to 6 (#422)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5 to 6.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-01 17:17:55 -04:00
dependabot[bot] e8c01c7893 Bump net.ltgt.errorprone from 5.1.0 to 5.1.1 (#424) 2026-09-01 17:26:24 +00:00
dependabot[bot] 1675952e94 Bump gradle-wrapper from 9.6.1 to 9.7.1 (#425) 2026-09-01 17:26:08 +00:00
dependabot[bot] 35e39ffd3e Bump org.jspecify:jspecify from 1.0.0 to 1.0.1 (#426) 2026-09-01 17:26:01 +00:00
Adam 7da27a0da0 Clean up new errorprone warnings (#419)
MenuType is no longer an enum
Offhand is fine with equality check
InventoryManager really does intend to check if the references match
2026-08-11 16:41:41 +00:00
dependabot[bot]andJikoo 180ceaf7e1 Bump com.gradleup.shadow from 9.4.3 to 9.6.1 (#418)
* Bump com.gradleup.shadow from 9.4.3 to 9.6.1

Bumps [com.gradleup.shadow](https://github.com/GradleUp/shadow) from 9.4.3 to 9.6.1.
- [Release notes](https://github.com/GradleUp/shadow/releases)
- [Commits](https://github.com/GradleUp/shadow/compare/9.4.3...9.6.1)

---
updated-dependencies:
- dependency-name: com.gradleup.shadow
  dependency-version: 9.6.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* Don't bother excluding projects

This is a piece of legacy configuration for the old project structure where modules were reflectively loaded.

---------

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>
2026-08-11 16:32:13 +00:00
dependabot[bot] e608233ad9 Bump softprops/action-gh-release from 3.0.1 to 3.0.2 (#416)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 3.0.1 to 3.0.2.
- [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/v3.0.1...v3.0.2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.2
  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>
2026-08-11 08:10:00 -04:00
R00tB33rMan d787516fce Fix search commands reporting only the first matching player (#414)
- Resolves https://github.com/Jikoo/OpenInv/issues/413
- Regression introduced by: https://github.com/Jikoo/OpenInv/pull/311

Before the regression, `SearchInvCommand` used two nested loops: an outer loop
over online players and an inner loop over the inventory contents. The
`break` lived in the **inner** loop, ending the per-item count-up once the
requested threshold was reached.

That commit extracted the inner loop into `SearchHelper.findMatch(...)` but
left the `break` behind. It now sits directly inside the **outer**
`for (Player player : ...)` loop, so the entire player scan terminates at
the first match.

I could be wrong about the intentionality of this `break` statement; however, in my eyes, this looks correct!
2026-07-19 17:40:48 -04:00
dependabot[bot] 50c522b8f6 Bump actions/checkout from 6 to 7 (#409)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-01 13:21:34 -04:00
dependabot[bot] 0192f5a6a9 Bump gradle-wrapper from 9.5.1 to 9.6.1 (#410) 2026-07-01 17:18:05 +00:00
dependabot[bot] 8123679c46 Bump com.gradleup.shadow from 9.4.2 to 9.4.3 (#411) 2026-07-01 17:17:41 +00:00
dependabot[bot] 1d37aad00a Bump com.google.errorprone:error_prone_core from 2.49.0 to 2.50.0 (#412) 2026-07-01 17:17:25 +00:00
dependabot[bot] 27a6e59682 Bump softprops/action-gh-release from 3.0.0 to 3.0.1 (#408)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 3.0.0 to 3.0.1.
- [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/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.1
  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>
2026-07-01 13:14:09 -04:00
Jikoo f72e22c2a1 Bump version to 5.3.4-SNAPSHOT for development 2026-06-28 10:24:36 -04:00
Jikoo 55053dacbf Bump version to 5.3.3 for release 2026-06-28 10:24:30 -04:00
KalkaandJikoo dacb07bd41 Update to Minecraft 26.2 (#406)
* Add support for Minecraft 26.2
* Update resource pack version
* Drop support for Minecraft 1.21.6-1.21.8
* Drop internal version listing

---------

Co-authored-by: Jikoo <jikoo.games@gmail.com>
2026-06-28 10:17:18 -04:00
dependabot[bot] 06d3fd7df9 Bump org.slf4j:slf4j-api from 2.0.17 to 2.0.18 (#404) 2026-06-03 01:11:53 +00:00
dependabot[bot] 4acf43b0fe Bump gradle-wrapper from 9.5.0 to 9.5.1 (#402) 2026-06-03 01:11:49 +00:00
dependabot[bot] a570001eaf Bump com.gradleup.shadow from 9.4.1 to 9.4.2 (#403) 2026-06-03 01:10:57 +00:00
Jikoo 70c53521c1 Update release template 2026-05-22 11:01:44 -04:00
Jikoo 9021ef9a22 Explicitly disable Modrinth unfeaturing
Docs suggest that this should not be necessary if featured management is not enabled, but here we are.
2026-05-22 10:55:36 -04:00
Jikoo 722ab4d2a3 Bump version to 5.3.3-SNAPSHOT for development 2026-05-22 10:05:45 -04:00
Jikoo 5f04575780 Bump version to 5.3.2 for release 2026-05-22 10:05:43 -04:00
Jikoo 0f5368b134 Include release notes on Hangar 2026-05-22 10:05:09 -04:00
Camden Cove b43a32139d Fix missed copy sending contents to viewers (#401) 2026-05-18 16:23:34 -04:00
R00tB33rMan 98d819c49a Fix inventory access using incorrect schedulers (#400) 2026-05-17 13:09:59 +00:00
tonyjamesstark 8f058fb206 Change format of searchchest (#393)
Displays name if available, shows one entry per line.
2026-05-04 16:16:59 -04:00
dependabot[bot] 60a32b7af5 Bump softprops/action-gh-release from 2.6.1 to 3.0.0 (#395)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.6.1 to 3.0.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.6.1...v3.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 18:52:41 -04:00
dependabot[bot] 61f7d5cee5 Bump actions/github-script from 8 to 9 (#394)
Bumps [actions/github-script](https://github.com/actions/github-script) from 8 to 9.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v8...v9)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '9'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-01 18:52:06 -04:00
dependabot[bot] 3d65681c6f Bump com.google.errorprone:error_prone_core from 2.48.0 to 2.49.0 (#398) 2026-05-01 20:43:44 +00:00
dependabot[bot] cf2f53f9e9 Bump gradle-wrapper from 9.4.1 to 9.5.0 (#397) 2026-05-01 20:43:22 +00:00
dependabot[bot] 95ae1cfa0a Bump net.ltgt.errorprone from 4.4.0 to 5.1.0 (#396) 2026-05-01 20:41:17 +00:00
Jikoo 07451de7a3 Bump version to 5.3.2-SNAPSHOT for development 2026-04-21 21:34:03 -04:00
Jikoo 872e14e1a6 Bump version to 5.3.1 for release 2026-04-21 21:33:41 -04:00
Adam f5e534f758 Port Bukkit data fix to Spigot (#391) 2026-04-22 01:23:07 +00:00
Adam 16a4c8f71b Update to Minecraft 26.1 (#385)
Also fixes issues with saving default Bukkit data

Closes #384
Closes #389
2026-04-21 21:05:00 -04:00
dependabot[bot] 7f4216995c Bump softprops/action-gh-release from 2.5.0 to 2.6.1 (#386)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.5.0 to 2.6.1.
- [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.5.0...v2.6.1)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.6.1
  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>
2026-04-02 09:32:35 -04:00
dependabot[bot] f945868a21 Bump com.gradleup.shadow from 9.3.2 to 9.4.1 (#388) 2026-04-01 22:03:46 +00:00
Adam ee923c0881 Upgrade Gradle wrapper to 9.4.1 (#383) 2026-03-22 16:02:16 +00:00
Adam 80d48ed218 Improve InventoryHolder handling (#382)
* Improve InventoryHolder handling
  * Don't snapshot holder on Paper
  * Ensure viewer has silent enabled first to reduce impact on Spigot
* Remove BlockState reflection
2026-03-20 23:45:18 +00:00
dependabot[bot] 8de1f778f0 Bump net.md-5:SpecialSource from 1.11.5 to 1.11.6 (#379) 2026-03-02 17:24:53 +00:00
dependabot[bot] 8955882737 Bump com.google.errorprone:error_prone_core from 2.46.0 to 2.48.0 (#378) 2026-03-02 17:24:13 +00:00
dependabot[bot] 037b3f89cd Bump com.gradleup.shadow from 9.3.1 to 9.3.2 (#377) 2026-03-02 17:23:37 +00:00
dependabot[bot] 43b4360bb6 Bump org.jetbrains:annotations from 26.0.2-1 to 26.1.0 (#376) 2026-03-02 17:23:03 +00:00
dependabot[bot] e03c43555d Bump actions/upload-artifact from 6 to 7 (#375)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 12:20:05 -05:00
dependabot[bot] 43a13e6254 Bump actions/download-artifact from 7 to 8 (#374)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-02 12:19:50 -05:00
Jikoo ab3dddbc19 Update Spigot revision
This appears to compile, but will need testing before a release. Spigot removed the renames they used to apply to all internal classes.
2026-03-02 10:47:10 -05:00
Jikoo d35fc6713b Add links to all official download sources
Some are still pending approval that probably won't happen until Monday, but oh well.
2026-01-18 01:05:43 -05:00
Jikoo 9c8a16adc9 Fix external releases
Add loaders to mc-publish
Fix version parsing for Hangar. The trim command was added in jq 1.8.
2026-01-18 00:37:10 -05:00
Jikoo b93900cf3b Bump version to 5.3.1-SNAPSHOT for development 2026-01-17 23:57:47 -05:00
Jikoo 63fccdcf91 Bump version to 5.3.0 for release 2026-01-17 23:57:38 -05:00
dependabot[bot]andJikoo 960acbfe9d Bump errorprone versions (#370)
* Bump com.google.errorprone:error_prone_core from 2.45.0 to 2.46.0

Bumps [com.google.errorprone:error_prone_core](https://github.com/google/error-prone) from 2.45.0 to 2.46.0.
- [Release notes](https://github.com/google/error-prone/releases)
- [Commits](https://github.com/google/error-prone/compare/v2.45.0...v2.46.0)

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

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

* Bump errorprone-gradle-plugin to 4.4.0

Resolves issues with missing flag required for errorprone-core

---------

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>
2026-01-18 02:37:20 +00:00
dependabot[bot] 3c904499c5 Bump com.gradleup.shadow from 9.2.2 to 9.3.1 (#369) 2026-01-17 16:54:05 +00:00
Adam baa6b3d734 Add profile cache (#368)
Instead of reading every single player file from disk when doing a fuzzy match, we now do a single lookup from a SQLite database. The database is populated once with all existing players' names on startup, then maintained when players join. This is a massive performance boost for servers that have high player counts or run on spinning disks.

Lookups now require that the first character of the target player's name be correct (case-insensitive). For Geyser-like names, this extends to the first character of their real name. For example, "XxXJohnMinecraftXxX" can no longer be located by a "johnminecraft" search. A search for "xjohnminecraft" could be used instead. A Geyser player ".FranMinecraft" would require ".f" to be eligible, i.e. "franmc" cannot produce the correct result while ".franmc" might.
2026-01-17 11:50:23 -05:00
Adam 7f6c339e06 Add option to check permissions with toggle (#361)
* Promote Permissions to API
* Document permissions
* Add method for checking a toggle with permissions
2026-01-03 12:20:58 -05:00
dependabot[bot] 00736c8d30 Bump actions/download-artifact from 6 to 7 (#364)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-01 12:12:35 -05:00
dependabot[bot] cb1ec77c24 Bump actions/upload-artifact from 5 to 6 (#363)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-01 12:12:25 -05:00
dependabot[bot] c8c8a72819 Bump actions/cache from 4 to 5 (#362)
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-01 12:12:14 -05:00
Alessandro 16f91ddc9f feat: add use sub-permissions 2026-01-01 08:55:43 -05:00
Adam c83511e475 Upload to Modrinth and Hangar (#360)
* Replace curseforge-upload with mc-publish
* Use anchors for common configurations
* Convert script to a shared run step
2025-12-31 14:39:53 -05:00
Jikoo c095a78013 Bump version to 5.2.1-SNAPSHOT for development 2025-12-17 15:29:26 -05:00
Jikoo 4441cdcf04 Bump version to 5.2.0 for release 2025-12-17 15:29:23 -05:00
Adam 9940b9d840 Update to 1.21.11 (#358) 2025-12-17 15:11:56 -05:00
dependabot[bot] fb409f8024 Bump actions/checkout from 5 to 6 (#354)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-14 17:48:28 -05:00
dependabot[bot] 08b43a8aca Bump softprops/action-gh-release from 2.4.1 to 2.5.0 (#355)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.4.1 to 2.5.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.4.1...v2.5.0)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.5.0
  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-12-14 17:08:30 -05:00
dependabot[bot] 3df3d41a7f Bump com.google.errorprone:error_prone_core from 2.43.0 to 2.45.0 (#356) 2025-12-01 20:21:44 +00:00
dependabot[bot] 16cba8a137 Bump actions/upload-artifact from 4 to 5 (#348)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-03 09:59:45 -05:00
dependabot[bot] f1b86b4eb1 Bump softprops/action-gh-release from 2.3.3 to 2.4.1 (#349)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.3 to 2.4.1.
- [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.3.3...v2.4.1)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.4.1
  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-11-03 09:59:24 -05:00
dependabot[bot] 172e1cd55b Bump actions/download-artifact from 5 to 6 (#350)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-03 09:58:40 -05:00
Adam d8b5aa145b Improve equal access behaviors (#346)
* Extract common chest code, expose view-only state
* Add prefix for view-only state
* Add nodes for behavior of accessing equal
* Include permission-based denial feedback
2025-11-03 09:51:00 -05:00
dependabot[bot] 2ffff99acf Bump com.google.errorprone:error_prone_core from 2.42.0 to 2.43.0 (#351) 2025-11-01 17:24:15 +00:00
Jikoo 743c46f322 Bump version to 5.1.16-SNAPSHOT for development 2025-10-11 09:48:58 -04:00
Jikoo e3285a6d61 Bump version to 5.1.15 for release 2025-10-11 09:48:35 -04:00
Jikoo 806fb848a7 Add line break between server implementations
I noticed this was a problem the first update and have been forgetting and then manually fixing it on Bukkit since. A travesty.
2025-10-11 09:46:34 -04:00
Adam b269a5ed3c Fix /openinv on Spigot (#342) 2025-10-11 13:35:59 +00:00
Jikoo 8c102c4400 Bump version to 5.1.15-SNAPSHOT for development 2025-10-08 15:01:55 -04:00
Jikoo 080d02f93f Bump version to 5.1.14 for release 2025-10-08 15:01:31 -04:00
Adam 54f9d30a89 Update to 1.21.9/1.21.10 (#339) 2025-10-08 15:00:13 -04:00
dependabot[bot] 6063959362 Bump actions/github-script from 7 to 8 (#336)
Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-03 09:03:50 -04:00
dependabot[bot] d80b8a6f4d Bump gradle/actions from 4 to 5 (#334)
Bumps [gradle/actions](https://github.com/gradle/actions) from 4 to 5.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/v4...v5)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-03 09:03:22 -04:00
dependabot[bot] 13621901ad Bump softprops/action-gh-release from 2.3.2 to 2.3.3 (#335)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.3.2 to 2.3.3.
- [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.3.2...v2.3.3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.3.3
  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>
2025-10-03 08:49:37 -04:00
dependabot[bot] 20cc6e004e Bump com.gradleup.shadow from 9.1.0 to 9.2.2 (#337) 2025-10-01 17:25:00 +00:00
dependabot[bot] 80585dfc07 Bump com.google.errorprone:error_prone_core from 2.41.0 to 2.42.0 (#338) 2025-10-01 17:24:53 +00:00
dependabot[bot] 9e37b42c63 Bump actions/download-artifact from 4 to 5 (#328)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-03 10:08:58 -04:00
dependabot[bot] 58be5cffc9 Bump actions/setup-java from 4 to 5 (#330)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-03 13:14:45 +00:00
dependabot[bot] e8c8e625c0 Bump actions/checkout from 4 to 5 (#327)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-03 09:10:37 -04:00
dependabot[bot] 0ee3f8c1b2 Bump com.gradleup.shadow from 8.3.8 to 9.1.0 (#331) 2025-09-02 12:15:47 +00:00
dependabot[bot] 99f9099981 Bump org.jetbrains:annotations from 26.0.2 to 26.0.2-1 (#329) 2025-09-02 11:19:21 +00:00
Jikoo 70ae432158 Fix missing override annotations 2025-08-07 09:06:35 -04:00
dependabot[bot] ad05f427fd Bump com.google.errorprone:error_prone_core from 2.39.0 to 2.41.0 (#325) 2025-08-01 17:36:44 +00:00
Adam 252e93f872 Update for new Paper changes (#324)
* Fix potential desync if a plugin triggers a slot update under certain circumstances
* Increase visibility to match changes to superclass
2025-07-28 12:55:08 +00:00
Jikoo 0267090cb6 Bump version to 5.1.14-SNAPSHOT for development 2025-07-18 11:43:17 -04:00
Jikoo 1e26c33311 Bump version to 5.1.13 for release 2025-07-18 11:43:15 -04:00
Adam 34f5319f48 Add 1.21.8 support (#321)
* Add 1.21.8 to supported versions
* Correct resource pack version range
2025-07-18 15:33:52 +00:00
Jikoo 642e287507 Bump version to 5.1.13-SNAPSHOT for development 2025-07-01 21:00:21 -04:00
Jikoo 4504ba2b99 Bump version to 5.1.12 for release 2025-07-01 20:59:38 -04:00
dependabot[bot] 9691d16506 Bump softprops/action-gh-release from 2.2.2 to 2.3.2 (#316)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.2.2 to 2.3.2.
- [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.2.2...v2.3.2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.3.2
  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-07-01 20:58:24 -04:00
Adam 1897790db5 Update to 1.21.6/1.21.7 (#315) 2025-07-01 20:57:24 -04:00
dependabot[bot] cad76c6559 Bump com.google.errorprone:error_prone_core from 2.38.0 to 2.39.0 (#318) 2025-07-01 17:50:13 +00:00
dependabot[bot] 119ff48461 Bump com.gradleup.shadow from 8.3.6 to 8.3.8 (#317) 2025-07-01 17:49:19 +00:00
Jikoo b589f79e57 Update gradle wrapper to 8.14 2025-05-21 10:07:30 -04:00
Jikoo f742cf5b77 Update paperweight-userdev for new bundle version 2025-05-21 10:07:30 -04:00
Jikoo 19d030dd4d Improve clear parameter handling
Don't tab complete parameter for those who can't target others
Don't load players for those who can't target others
2025-05-21 10:07:30 -04:00
Jikoo 17dec41fea Bump version to 5.1.12-SNAPSHOT for development 2025-05-20 09:23:04 -04:00
Jikoo b5599450f5 Bump version to 5.1.11 for release 2025-05-20 09:23:01 -04:00
Adam b2c13b40e2 Improve feedback, code health (#312)
* Drop legacy translations
* Drop legacy viewer handling
* Improve lookup feedback
* Deprecate ISpecialInventory#setPlayerOffline
* Fix default value being provided
2025-05-20 13:22:35 +00:00
Adam 03c0fea7ab Search shulker boxes and bundles (#311) 2025-05-19 11:31:49 -04:00
Jikoo 9ca5b2d826 Apply errorprone suggestions 2025-05-19 06:20:03 -04:00
Jikoo 7a9bb11133 Re-add errorprone
Dropped during swap to Gradle
2025-05-19 05:40:43 -04:00
Jikoo 8647f8e156 Autoformat 2025-05-18 11:09:30 -04:00
Jikoo 2a07673b1a Add .gitattributes and .editorconfig 2025-05-18 11:09:16 -04:00
Adamandtrard 8418cb14fe Add /clearinv and /clearender (#310)
Co-authored-by: trard <trardone@gmail.com>
2025-05-18 08:57:31 -04:00
Jikoo 6647334072 Fix missed override
Closes #307
2025-05-02 00:52:42 -04:00
dependabot[bot] e479a5eaa7 Bump softprops/action-gh-release from 2.2.1 to 2.2.2 (#306)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.2.1 to 2.2.2.
- [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.2.1...v2.2.2)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: 2.2.2
  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>
2025-05-01 13:35:11 -04:00
Jikoo 1c40091cfa Bump version to 5.1.11-SNAPSHOT for development 2025-04-25 11:50:11 -04:00
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
Jikoo fbdda9637e Bump version to 5.1.0 for release 2024-08-10 12:53:50 -04:00
Jikoo 336aee88e2 Remove reliance on CB conversions
Closes #238
2024-08-10 12:50:46 -04:00
Adam d50b7433c7 Add equal access behavior config (#237) 2024-08-10 15:41:39 +00:00
Jikoo e64261669b More responsibility reduction 2024-08-07 12:54:50 -04:00
Jikoo e8ab490d95 Declare API version for persistence addon 2024-08-07 12:54:16 -04:00
Jikoo 5eaf6243fb Fix modification state being clobbered 2024-08-07 12:54:08 -04:00
Jikoo 5e2cf56d6f Fix "adding" 0 items to last slot
Closes #232
2024-08-07 12:43:06 -04:00
Adam 0f0bde3e49 Move no-snapshot method out of API (#236) 2024-08-07 14:49:37 +00:00
冰砚炽 4198b7f22a Fix white stained glass pane texture (#235) 2024-08-06 09:36:02 -04:00
Newwind 8c15b3d078 Try to get block state without taking snapshots (#234) 2024-08-05 13:25:08 -04:00
dependabot[bot] 335d849525 Bump softprops/action-gh-release from 2.0.6 to 2.0.8 (#231)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.6 to 2.0.8.
- [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.6...v2.0.8)

---
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-08-01 15:57:37 -04:00
dependabot[bot] 0fb9e5eed3 Bump org.apache.maven.plugins:maven-jar-plugin from 3.4.1 to 3.4.2 (#229) 2024-08-01 17:36:47 +00:00
dependabot[bot] fe11217a27 Bump com.google.errorprone:error_prone_core from 2.28.0 to 2.29.2 (#230) 2024-08-01 17:36:34 +00:00
Adam 5fea7f3e02 Make toggles transient (#227)
The addon OITogglePersist restores old behavior.
2024-07-28 15:03:44 +00:00
Jikoo dbceff086a Reduce responsibility of main class
This should make the plugin a ton more testable (though I don't have it in me right now to write tests).
2024-07-25 11:57:34 -04:00
Jikoo 64899e5c3d Simplify inventory handling
* Rename view-only inventory for clarity
* Repackage menus and Bukkit implementations for clarity
* Reduce duplicate code somewhat
2024-07-15 14:21:55 -04:00
Adam bca080be7c Restrict view-only further (#226)
* Top inventory now rejects edits entirely
  * This prevents potential issues with plugins ignoring cancelled event status and then doing a strange mix of mismatched queries (like getItem into setContents).
  * It is not feasible/worthwhile to do this with the bottom inventory; many plugins don't use `InventoryView#getBottomInventory`. They use `Player#getInventory` instead.
2024-07-15 10:01:42 -04:00
Jikoo 09842fd168 Bump version to 5.0.1-SNAPSHOT for development 2024-07-12 12:07:12 -04:00
Jikoo 7d63500c79 Bump version to 5.0.0 for release 2024-07-12 12:06:35 -04:00
Jikoo 7fbb9a9da6 Only build resource pack on change/release 2024-07-12 11:58:36 -04:00
Adam 0597d7d9db Improve plugin compatibility (#224)
* Yield correct item for click event slot
* Improve raw slot conversion
2024-07-12 15:33:35 +00:00
Jikoo 1f2c2b6a49 Refactor new internals slightly
Repackage inventory -> container (because internally Inventory is a player container)
Old classes -> 2 spaces

Will eventually update project style and add a .editorconfig, but not right now.
2024-07-12 10:40:35 -04:00
Adam 59fff35fcc Improve language manager (#223)
* Add fallthrough to "parent" language
  * I am aware that the "correct" parent for Portuguese is likely `pt_pt`, but as no one has submitted it, welcome to Brazil.
* Use English for lowercasing locale name
  * Fixes potential issues locating translations for certain languages on systems in other languages
* Migrate locales to "locale" subdirectory to reduce clutter
* Fix container settings getting clobbered/not suggested correctly/not editable
  * This was due to a quirk of the YAML parser - OpenInv was looking for `'on'` and `'off'` but translation files provided `'true'` and `'false'` due to the paths being interpreted as truthy. The built-in translations have had their paths quoted to fix this. You will need to manually relocate these if you have custom translations.
2024-07-12 10:32:41 -04:00
Adam bb80d93510 Improve PlayerInventory implementation (#222)
* Fix storage contents including other items
* Fix regular contents including fake slots
* Fix type not being PLAYER
* Prefer slot indices to magic constants
2024-07-12 00:25:45 +00:00
Adam 8bf63e7f12 Internalize view-only status (#221) 2024-07-11 12:34:58 -04:00
Adam ebc063d34d Reimplement ISpecialEnderChest (#220) 2024-07-11 00:33:47 -04:00
Adam b895cb400b Remove deprecated methods, restructure (#219) 2024-07-10 14:40:44 +00:00
Adam 1cfa9bdd81 Search all slots with /searchinv (#218) 2024-07-07 14:05:59 +00:00
Adam e5b4e81ff9 Overhaul permissions, add slot perms (#217) 2024-07-07 09:03:35 -04:00
Adam e39dbc0044 Improve customizability, add resource pack (#215) 2024-07-05 08:43:34 -04:00
Adam 09b3f83816 Improve player inventory handling (#209)
This is a from-scratch rewrite of the SpecialPlayerInventory.
* Prettify layout
  * Reorganized contents
  * Uses client-side placeholders in nonstandard slots to clarify usage
* Add cursor and crafting slots
* Remove duplicate slots from own inventory
* Reimplement shift-clicking, improve drag support
  * Prioritize equipping gear with quick-move
* Use client translations for title
* Update off-hand when in other containers
  * Modifications to the off-hand would not be sent to clients while they had another container open. Updates occurred immediately on container exit.
2024-07-03 10:11:44 -04:00
dependabot[bot] 7cca23f7b1 Bump softprops/action-gh-release from 2.0.5 to 2.0.6 (#212)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.5 to 2.0.6.
- [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.5...v2.0.6)

---
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-07-01 14:55:41 -04:00
dependabot[bot] dfdef394ce Bump org.apache.maven.plugins:maven-dependency-plugin (#211) 2024-07-01 17:56:16 +00:00
Jikoo 6325b353dc Bump version to 4.4.8-SNAPSHOT for development 2024-06-16 11:53:43 -04:00
Jikoo 7a07ee0b3d Bump version to 4.4.7 for release 2024-06-16 11:53:21 -04:00
Adam e97ac3db88 Fix inventory view construction on 1.21 (#208) 2024-06-16 15:47:09 +00:00
Jikoo f72b6749c4 Bump version to 4.4.7-SNAPSHOT for development 2024-06-14 12:05:47 -04:00
Jikoo f1bcb496cf Bump version to 4.4.6 for release 2024-06-14 12:05:28 -04:00
Adam 89beeca853 Update to 1.21 (#206)
* Update to 1.21

* Add new InventoryView methods
2024-06-14 12:02:11 -04:00
dependabot[bot] 282f8630ab Bump softprops/action-gh-release from 2.0.4 to 2.0.5 (#203)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2.0.4 to 2.0.5.
- [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.4...v2.0.5)

---
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-06-01 17:07:48 -04:00
dependabot[bot] 8f374ef2a5 Bump com.github.jikoo:planarwrappers from 3.2.0 to 3.2.2 (#201)
Bumps [com.github.jikoo:planarwrappers](https://github.com/Jikoo/PlanarWrappers) from 3.2.0 to 3.2.2.
- [Commits](https://github.com/Jikoo/PlanarWrappers/compare/3.2.0...3.2.2)

---
updated-dependencies:
- dependency-name: com.github.jikoo:planarwrappers
  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-06-01 17:06:46 -04:00
dependabot[bot] 3d81fdd0ca Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.3 to 3.6.0 (#202) 2024-06-01 17:58:26 +00:00
Jikoo 2dde9ba28f Bump version to 4.4.6-SNAPSHOT for development 2024-05-18 17:22:15 -04:00
Jikoo 96d2d2482a Bump version to 4.4.5 for release 2024-05-18 17:21:47 -04:00
Jikoo e543053eff Fix saving issue exacerbated by #183
Closes #199
2024-05-18 17:20:51 -04:00
Jikoo 8134887666 Bump version to 4.4.5-SNAPSHOT for development 2024-05-13 18:25:36 -04:00
Jikoo 7ee6206ffb Bump version to 4.4.4 for release 2024-05-13 18:25:21 -04:00
Adam b81eab8191 Update to 1.20.6 (#198)
* Fix version detection on Paper
* Fix offline player changes being overwritten
2024-05-13 18:10:13 -04:00
dependabot[bot] 8b71d3549f Bump dsaltares/fetch-gh-release-asset from 1.1.1 to 1.1.2 (#197)
Bumps [dsaltares/fetch-gh-release-asset](https://github.com/dsaltares/fetch-gh-release-asset) from 1.1.1 to 1.1.2.
- [Release notes](https://github.com/dsaltares/fetch-gh-release-asset/releases)
- [Commits](https://github.com/dsaltares/fetch-gh-release-asset/compare/1.1.1...1.1.2)

---
updated-dependencies:
- dependency-name: dsaltares/fetch-gh-release-asset
  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-05-07 21:00:32 -04:00
dependabot[bot] dd82f59516 Bump pascalgn/automerge-action from 0.16.2 to 0.16.3 (#196)
Bumps [pascalgn/automerge-action](https://github.com/pascalgn/automerge-action) from 0.16.2 to 0.16.3.
- [Release notes](https://github.com/pascalgn/automerge-action/releases)
- [Commits](https://github.com/pascalgn/automerge-action/compare/v0.16.2...v0.16.3)

---
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-05-07 21:00:11 -04:00
Adam 80d3fb72e6 Update to 1.20.5/1.20.6 (#193)
* Move to API method for shulker expansion check
* Update to 1.20.6
  * Drop 1.20.2 support
* Fix containment check
* Drop 1.19.4 to compile without redoing Actions
2024-05-07 20:59:28 -04:00
dependabot[bot] 2ed23b24e5 Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.2 to 3.5.3 (#195) 2024-05-01 17:11:09 +00:00
dependabot[bot] b6d61d4cc9 Bump softprops/action-gh-release from 0.1.15 to 2.0.4 (#191)
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 0.1.15 to 2.0.4.
- [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/v0.1.15...v2.0.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-01 22:03:00 -04:00
dependabot[bot] c3f7eaebf4 Bump org.apache.maven.plugins:maven-compiler-plugin (#190) 2024-04-01 17:21:51 +00:00
dependabot[bot] 52c90e1cea Bump org.apache.maven.plugins:maven-assembly-plugin from 3.6.0 to 3.7.1 (#189) 2024-04-01 17:20:43 +00:00
dependabot[bot] d887ff8eae Bump hmarr/auto-approve-action from 3.2.1 to 4.0.0 (#187)
Bumps [hmarr/auto-approve-action](https://github.com/hmarr/auto-approve-action) from 3.2.1 to 4.0.0.
- [Release notes](https://github.com/hmarr/auto-approve-action/releases)
- [Commits](https://github.com/hmarr/auto-approve-action/compare/v3.2.1...v4.0.0)

---
updated-dependencies:
- dependency-name: hmarr/auto-approve-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-02 01:14:49 -05:00
dependabot[bot] 0d5fb6a0ef Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.1 to 3.5.2 (#186) 2024-03-01 17:40:32 +00:00
Adam 6f8a6e868a Improve compatibility with custom NBT (#183) 2024-02-16 22:41:01 -05:00
Jikoo 70c1ecfc09 Bump version to 4.4.4-SNAPSHOT for development 2024-02-08 13:11:46 -05:00
Jikoo 176272a4b9 Bump version to 4.4.3 for release 2024-02-08 13:11:18 -05:00
Adam c102e6650e Fix game type not loading (#180) 2024-02-08 13:07:23 -05:00
dependabot[bot] c8fee82291 Bump pascalgn/automerge-action from 0.15.6 to 0.16.2 (#179)
Bumps [pascalgn/automerge-action](https://github.com/pascalgn/automerge-action) from 0.15.6 to 0.16.2.
- [Release notes](https://github.com/pascalgn/automerge-action/releases)
- [Commits](https://github.com/pascalgn/automerge-action/compare/v0.15.6...v0.16.2)

---
updated-dependencies:
- dependency-name: pascalgn/automerge-action
  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-02-08 13:02:28 -05:00
dependabot[bot] 0ae9982ce5 Bump actions/download-artifact from 3 to 4 (#177)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-01 16:26:41 -05:00
dependabot[bot] 59d358d907 Bump actions/upload-artifact from 3 to 4 (#176)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-01 16:26:27 -05:00
dependabot[bot] 2621181c5d Bump org.apache.maven.plugins:maven-compiler-plugin (#178) 2024-01-01 18:07:35 +00:00
Jikoo 48d044af8c Bump version to 4.4.3-SNAPSHOT for development 2023-12-29 10:05:02 -05:00
Jikoo 9a7623c209 Bump version to 4.4.2 for release 2023-12-29 10:04:28 -05:00
Adam 78354fdf68 Add support for 1.20.4 (#173) 2023-12-29 10:01:54 -05:00
dependabot[bot] 859179d1fc Bump actions/setup-java from 3 to 4 (#168)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 3 to 4.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 18:03:17 -05:00
dependabot[bot] 79f53e9086 Bump actions/github-script from 6 to 7 (#169)
Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-01 18:02:30 -05:00
dependabot[bot] c14d777519 Bump org.jetbrains:annotations from 24.0.1 to 24.1.0 (#170) 2023-12-01 17:51:58 +00:00
Jikoo 1829babe69 Bump version to 4.4.2-SNAPSHOT for development 2023-11-12 14:29:31 -05:00
Jikoo 5aa959f1ac Bump version to 4.4.1 for release 2023-11-12 14:29:11 -05:00
Adam bc7e13f245 Expand preserved old data (#166)
* Fix session timestamps being erroneously updated
* Fix vehicle possibly not being re-mounted
2023-11-12 12:34:32 -05:00
Adam 73edebff54 Fix loading world on Paper (#165)
Unify load process to reduce duplicate code and risk of missed cases
2023-11-12 12:24:19 -05:00
dependabot[bot] faf1d387e9 Bump org.apache.maven.plugins:maven-dependency-plugin (#161) 2023-11-01 17:32:32 +00:00
Jikoo deed277376 Bump version to 4.4.1-SNAPSHOT for development 2023-10-04 22:19:02 -04:00
Jikoo 28fc5aea70 Bump version to 4.4.0 for release 2023-10-04 22:18:38 -04:00
Jikoo bfcf0233fa Fix ender chests not being silent 2023-10-04 22:15:35 -04:00
dependabot[bot] 3cf1aae89b Bump actions/checkout from 3 to 4 (#158)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-04 21:52:28 -04:00
dependabot[bot] db62878681 Bump org.apache.maven.plugins:maven-shade-plugin from 3.5.0 to 3.5.1 (#159) 2023-10-01 17:45:00 +00:00
Jikoo 21cd52c166 Fix probable NPE 2023-09-22 23:46:43 -04:00
dependabot[bot] 9d715b61c0 Bump maven-shade-plugin from 3.4.1 to 3.5.0 (#153)
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.4.1 to 3.5.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.1...maven-shade-plugin-3.5.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  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>
2023-09-22 23:20:13 -04:00
Jikoo d636cdd839 Add support for 1.20.2 2023-09-22 23:16:22 -04:00
Jikoo 1bebdb5602 Add OpenPlayerSaveEvent
Closes #156
2023-09-22 23:06:05 -04:00
Jikoo 57caf85500 Update Spigot 2023-09-10 11:23:49 -04:00
XPYEX 922586c925 Improve support for non-UTF-8 systems (#151) 2023-06-17 07:35:21 -04:00
Jikoo 9ec7bebecb Bump version to 4.3.2-SNAPSHOT for development 2023-06-10 11:09:43 -04:00
Jikoo 5a7d1399b5 Bump version to 4.3.1 for release 2023-06-10 11:09:30 -04:00
Adam cc7481fa9a Update to Minecraft 1.20 (#148) 2023-06-10 11:06:41 -04:00
dependabot[bot] 70d20aceb6 Bump maven-dependency-plugin from 3.5.0 to 3.6.0 (#146) 2023-06-01 18:11:12 +00:00
dependabot[bot] ac52211ce4 Bump maven-assembly-plugin from 3.5.0 to 3.6.0 (#147) 2023-06-01 18:07:58 +00:00
dependabot[bot] 6f2bdccc89 Bump dsaltares/fetch-gh-release-asset from 1.1.0 to 1.1.1 (#143)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-01 15:28:40 -04:00
Adam 6f23ed2528 Remove OS declaration from workflow_call (#144) 2023-05-01 15:26:56 -04:00
Adam 2d36249dbb Fix split up actions (#141) 2023-04-25 16:51:21 -04:00
Adam 2f370ad641 Fix loading users in deleted worlds on Paper (#139) 2023-04-17 18:20:31 -04:00
dependabot[bot] 1233f587c5 Bump hmarr/auto-approve-action from 3.1.0 to 3.2.1 (#134)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-01 15:22:44 -04:00
dependabot[bot] 23ebea405e Bump annotations from 24.0.0 to 24.0.1 (#133)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-01 15:22:34 -04:00
Jikoo 1bde75f9b3 Bump version to 4.3.1-SNAPSHOT for development 2023-03-16 16:44:10 -04:00
Jikoo 6ee2546efd Bump version to 4.3.0 for release 2023-03-16 16:43:56 -04:00
Adam 0e6acdff36 Update to 1.19.4 (#130)
* Improve data load/save slightly
* Fix longstanding issue with opening players in deleted worlds on Paper
2023-03-16 16:30:43 -04:00
Adam 3d4bed04d5 Split up actions (#129)
* Refactor conditional dependent jobs into separate actions
* Fix incorrect usage of pull_request_target
* Fix Dependabot not merging due to labels being absent when issue is created
* Prettify supported version listing
2023-03-15 08:13:33 -04:00
dependabot[bot] e885bf9a65 Bump maven-assembly-plugin from 3.4.2 to 3.5.0 (#127)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-01 18:21:01 -05:00
dependabot[bot] e15c566b82 Bump maven-compiler-plugin from 3.10.1 to 3.11.0 (#128)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-01 18:20:50 -05:00
dependabot[bot] c1ffd09d50 Bump pascalgn/automerge-action from 0.15.5 to 0.15.6 (#126)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-01 18:20:31 -05:00
Adam bd5e47760e Fix duplicate closes swapping players to spectate (#125) 2023-02-18 14:10:36 -05:00
dependabot[bot] 2fe0322570 Bump maven-dependency-plugin from 3.4.0 to 3.5.0 (#123)
Bumps [maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/apache/maven-dependency-plugin/releases)
- [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.4.0...maven-dependency-plugin-3.5.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-dependency-plugin
  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>
2023-02-02 09:01:16 -05:00
dependabot[bot] 767476a759 Bump annotations from 23.1.0 to 24.0.0 (#122)
Bumps [annotations](https://github.com/JetBrains/java-annotations) from 23.1.0 to 24.0.0.
- [Release notes](https://github.com/JetBrains/java-annotations/releases)
- [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md)
- [Commits](https://github.com/JetBrains/java-annotations/compare/23.1.0...24.0.0)

---
updated-dependencies:
- dependency-name: org.jetbrains:annotations
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-02 09:01:06 -05:00
dependabot[bot] dc8e36d11f Bump annotations from 23.0.0 to 23.1.0 (#121)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-01 19:13:14 -05:00
Adam 79b2a97c9e Clamp /searchcontainer radius (#117)
Default max is 10 because that's the default server view distance.
2022-12-20 16:44:49 -05:00
Adam 81eb60f628 Improve language manager (#116)
* Clarify replacements internally to reduce the likelihood of messing up when including new ones
 * Fix missing keys not being written to disk for easy translation
 * Use a guessfile-like system where missing translation keys are inserted into a separate section for easy identification
 * Reduce verbosity of missing keys warning
2022-12-20 15:54:30 -05:00
Adam e39f092f14 Add lookup cache, option to open self with no args (#115)
Add a cache for the last 10 offline lookups done by OpenInv#matchPlayer(String) to reduce the performance hit of repeat opens
Add config option to open self with no-arg command
2022-12-20 11:54:12 -05:00
Jikoo 4816737b90 Bump version to 4.2.3-SNAPSHOT for development 2022-12-11 11:35:36 -05:00
Jikoo 86bf24e7af Bump version to 4.2.2 for release 2022-12-11 11:35:21 -05:00
Jikoo 381dbb2a78 Don't disable minimizeJar in internal modules
MSHADE-366 was fixed in maven-shade-plugin 3.4.1. This is technically unnecessary either way as the only things currently being shaded are all our own modules.
2022-12-10 16:11:00 -05:00
Jikoo 46f3396b47 Update to Minecraft 1.19.3
1.19.2 is still supported - 1.19.1 was only dropped because there was no Spigot NMS revision bump.
2022-12-10 15:58:39 -05:00
dependabot[bot] 7b16391a80 Bump hmarr/auto-approve-action from 2.4.0 to 3.1.0 (#113)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 09:47:23 -05:00
dependabot[bot] 801530490d Bump dsaltares/fetch-gh-release-asset from 1.0.0 to 1.1.0 (#111)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 09:46:55 -05:00
dependabot[bot] d7e78177e0 Bump softprops/action-gh-release from 0.1.14 to 0.1.15 (#110)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 09:46:26 -05:00
dependabot[bot] e5128fa8db Bump pascalgn/automerge-action from 0.15.3 to 0.15.5 (#109)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 09:46:12 -05:00
dependabot[bot] 1efc0d4904 Bump maven-shade-plugin from 3.4.0 to 3.4.1 (#112)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 09:45:59 -05:00
dependabot[bot] 961a900632 Bump maven-dependency-plugin from 3.3.0 to 3.4.0 (#108)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 09:45:43 -05:00
Adam 0120d35a9a Fix advancement-related memory leak (#104)
Fix memory leak with loaded players' advancements
Fix missing transaction transfer for player inventories
Fix incorrect transaction transfer for ender chests
Minor code health
2022-10-14 16:40:33 -04:00
Adam c443615c1d Fix cache never being used for Spigot dependencies (#105) 2022-10-14 13:45:26 -04:00
dependabot[bot] b872f94855 Bump maven-shade-plugin from 3.3.0 to 3.4.0 (#102)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-01 15:13:03 -04:00
dependabot[bot] 2679a520a5 Bump actions/checkout from 2 to 3 (#103)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-01 15:12:52 -04:00
Adam d0648cc463 Fix incorrect player's locale used in view title (#101) 2022-10-01 12:43:57 -04:00
dependabot[bot] 1ceaf296f2 Bump maven-compiler-plugin from 3.8.1 to 3.10.1 (#76)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-01 12:36:48 -04:00
dependabot[bot] 1e0fb7bdb1 Bump hmarr/auto-approve-action from 2.2.1 to 2.4.0 (#99)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-10 14:52:17 -04:00
Jikoo 8c7698b40e Bump version to 4.2.2-SNAPSHOT for development 2022-08-01 13:35:25 -04:00
Jikoo 6b38cb7201 Bump version to 4.2.1 for release 2022-08-01 13:35:00 -04:00
dependabot[bot] 23e2a7e534 Bump maven-assembly-plugin from 3.3.0 to 3.4.2 (#94)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-01 13:32:56 -04:00
Elioby 1093b6d911 Update to 1.19.1 (#89) 2022-07-30 21:04:40 -04:00
Jikoo 89db744113 Bump version to 4.2.1-SNAPSHOT for development 2022-06-07 19:41:52 -04:00
Jikoo fcb9ccb6e6 Bump version to 4.2.0 for release 2022-06-07 19:41:39 -04:00
Jikoo 9c934e440d Update main plugin to Java 17
All supported versions require Java 17, there's no reason the plugin should not.
Swap to Temurin for CI, Adopt is dead.
2022-06-07 19:31:23 -04:00
Jikoo c2d60642c3 Update to Minecraft 1.19
Drop support for 1.17 and outdated 1.18 builds.
2022-06-07 19:31:23 -04:00
Jikoo 0653d12e49 Code health pass for Java 16 2022-06-07 19:31:22 -04:00
Jikoo f34b342cdd Tab? The drink? 2022-06-07 19:31:22 -04:00
Jikoo 70d8999b48 Move translations to a separate package
Makes it easier to find non-language files as necessary
2022-06-07 19:31:22 -04:00
Jikoo a5b21cbbf5 Correct crossworld check 2022-06-07 19:31:22 -04:00
Jikoo 44e514d8c2 Don't close other inventories for opened player during close 2022-06-07 19:31:22 -04:00
Jikoo 6407f1d8e4 Reorganize members 2022-06-07 19:31:22 -04:00
Jikoo 0b761a6fc3 Continue migrating terminology to "container"
Clean up documentation a bit, prettify
2022-06-07 19:31:21 -04:00
Jikoo 6ad6e0c6bc Bump version to 4.2.0-SNAPSHOT 2022-06-07 19:31:21 -04:00
Jikoo fdf920062b Remove cache, add disable-offline-access config node
Fixes various permissions not being respected during login/logout with inventories already open. This will result in a performance hit for repeated open/closes - there will be significantly more disk I/O.

Closes lishid#171
Closes #56
2022-06-07 19:31:21 -04:00
Jikoo 9502b29fe3 Clean up compiler warnings
Compiler doesn't understand that api is provided by plugin during build process
2022-06-07 19:31:21 -04:00
Jikoo c9ba401a6e Don't inject player if unnecessary 2022-06-07 19:31:21 -04:00
Jikoo b6e8e2ba42 Extract some duplicate code from internal implementations 2022-06-07 19:31:20 -04:00
Jikoo ac00261afb Note Spigot requirement 2022-06-07 19:31:20 -04:00
Jikoo f21019eba8 Extract help to location of use 2022-06-07 19:31:20 -04:00
Jikoo 525982ce64 Harden event/internal access slightly 2022-06-07 19:31:20 -04:00
dependabot[bot] 65c3358b22 Bump pascalgn/automerge-action from 0.15.2 to 0.15.3 (#82)
Bumps [pascalgn/automerge-action](https://github.com/pascalgn/automerge-action) from 0.15.2 to 0.15.3.
- [Release notes](https://github.com/pascalgn/automerge-action/releases)
- [Commits](https://github.com/pascalgn/automerge-action/compare/v0.15.2...v0.15.3)

---
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>
2022-06-01 17:29:37 -04:00
dependabot[bot] d6152f2f24 Bump dsaltares/fetch-gh-release-asset from 0.0.5 to 1.0.0 (#83)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-01 17:28:42 -04:00
dependabot[bot] cc3d96590f Bump hmarr/auto-approve-action from 2.0.0 to 2.2.1 (#77)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-05-01 13:19:34 -04:00
Jikoo 60c1d91e25 Simplify Maven check
Apparently Dependabot actually manages language labels when multiple options are enabled. Convenient!
2022-04-29 13:50:32 -04:00
Jikoo 7903942b51 Correct Action indentation
Wish it was more clearly indicated that the action was failing anywhere but the actions tab. No notification even, kind of annoying to have actually made a PR to double-check and then still have it fail.
2022-04-29 13:40:00 -04:00
Adam 22407aa865 Update dependencies, add Dependabot (#75) 2022-04-29 13:32:48 -04:00
Max Lee 173f49680b Log error message when failing to save player data (#72) 2022-04-05 17:10:58 -04:00
Jikoo 3103e8822b Bump version to 4.1.11-SNAPSHOT for development 2022-03-11 08:31:26 -05:00
Jikoo a547ab79fb Bump version to 4.1.10 for release 2022-03-11 08:31:11 -05:00
Jikoo bb75c723a7 Actually build 1.18.2 module 2022-02-28 22:16:26 -05:00
Jikoo 9bb94be7e2 Update to 1.18.2 2022-02-28 22:08:35 -05:00
Jikoo f78a5b4fba Ignore PlayerInventory owner as a viewer
If online, owner is always a viewer. If offline, they are never a viewer. There's no reason to count them - keeps inventory permanently cached while online if opened, even if not in use.
2022-01-27 11:37:53 -05:00
Jikoo 4ca222f031 Remove deprecated API usage 2022-01-27 11:16:06 -05:00
Jikoo 4fdfe15caa Update viewers for closed silent inventories
Closes #60
2022-01-27 11:15:34 -05:00
Jikoo 3aa31391be Add trace to InstantiationException
Closes #59
2022-01-27 10:03:12 -05:00
Jikoo abd529be66 Clean up compilation instructions for Mojang mappings 2022-01-19 10:23:26 -05:00
Jikoo 027d4e5637 Bump Spigot version to 1.18.1 2022-01-19 10:21:54 -05:00
Jikoo f8fb8cd034 Reduce duplicate configuration details
Adapters must now declare a `spigot.version` property.

* Greatly simplifies and speeds up retrieval of Spigot versions
2022-01-19 10:11:16 -05:00
Jikoo 6a31015ee2 Bump version to 4.1.10-SNAPSHOT for development 2021-12-02 16:15:34 -05:00
Jikoo 3e2b601997 Bump version to 4.1.9 for release 2021-12-02 16:15:22 -05:00
Jikoo 160ae47bf5 Correct wrong item list usage 2021-12-02 16:13:37 -05:00
Jikoo 98fbcebfd9 Fix non-translatable component, wrong flag value 2021-12-02 15:44:19 -05:00
Jikoo d236dd27b3 Update to 1.18-pre5 2021-11-23 13:00:42 -05:00
Jikoo dd90b5281f Use Java 17 for CI 2021-11-17 10:41:53 -05:00
Jikoo b4601bb6c1 Fix cached Spigot dependencies never being used
Since plugins have not been downloaded, get always fails when running in offline mode.
2021-10-16 19:16:08 -04:00
Jikoo e3d9cea027 Fix spigot dependency parsing
* Re-add cache
* Work around reflection issues with mvn help:evaluate on Java 9+
2021-10-16 18:03:38 -04:00
Jikoo 556a8bcfce Drop 1.16.5 support to use Mojang mappings
Because there is no option to create a `remapped-mojang` artifact for 1.16.5 and the whole point of the scripts is to save me time, 1.16.5 support is being removed earlier than usual.

Also fixes issues with NMS-based shulker collision check.
Closes #36
2021-10-16 16:53:59 -04:00
Jikoo 8599997e03 Work towards fixing shulkers 2021-10-16 16:11:18 -04:00
Jikoo ea99bd5fd4 Extract duplicate API-only code 2021-07-28 11:28:08 -04:00
蘭蘭露 Flandre_tw 2f36a4d4dc Add Chinese Simplified translation (#35) 2021-06-28 20:05:55 -04:00
蘭蘭露 Flandre_tw e5ebf11d32 Add Chinese Traditional translation (#34) 2021-06-28 20:05:15 -04:00
Jikoo 4336b454b7 Fix potential CME closing viewers without access
Closes #33
2021-06-24 08:21:26 -04:00
Jikoo b198931f30 Bump version to 4.1.9-SNAPSHOT for development 2021-06-11 18:40:48 -04:00
Jikoo 64af4dddb0 Bump version to 4.1.8 for release 2021-06-11 18:40:39 -04:00
Jikoo f613c0a522 Remove redundant load 2021-06-11 18:39:19 -04:00
Jikoo 1cae4c7a8e Correct window id 2021-06-11 18:39:07 -04:00
Jikoo ff7243db6a Fix script issues
Temporarily explicitly declare Spigot dependencies
I don't have the time to deal with whatever the difference between remote and local bash/maven versions is right now.
2021-06-11 12:05:38 -04:00
Jikoo 07a8e3b973 Add 1.17 support 2021-06-11 10:50:50 -04:00
Jikoo 9fccea60f7 Update copyright 2021-06-11 10:50:36 -04:00
Jikoo 9a2b379a64 Escape changelog for YAML
With 1.17 releasing soon, we'll get to see if this was the issue very shortly.
Worst case scenario the yaml is still messed up, but since the changelog is now the last element, unless it's so badly malformed that the entire action can't run the file should still upload even if the changelog does not.
2021-06-08 11:38:26 -04:00
Jikoo 4d800361d8 Bump version to 4.1.8-SNAPSHOT for development 2021-05-29 09:51:39 -04:00
Jikoo 2cfc55813b Bump version to 4.1.7 for release 2021-05-29 09:51:25 -04:00
Jikoo 0f41701429 Update annotations 2021-05-26 08:17:11 -04:00
Jikoo 9e0ca479a5 Fix Bukkit data not being loaded while offline
Closes #16
2021-05-26 08:16:25 -04:00
Jikoo bd207e948a Simplify dependency management
Use dependencyManagement for versioning shared dependencies
Use pluginManagement for versioning and configuring plugins
2021-04-23 13:40:22 -04:00
Jikoo 30425d2baa Don't bother validating names
Fixes problems with players from Geyser not being obtainable when using FloodGate.
2021-04-21 14:57:46 -04:00
Jikoo e3acb5384a Deprecate API for removed notification configuration 2021-04-05 19:12:45 -04:00
Jikoo 6c4818dfd9 Add method to access owner
Makes handling ISpecialInventory ownership much more consistent with a lot less spaghetti.
2021-03-28 11:12:49 -04:00
Jikoo 5b18656485 Fix incorrect player name in title
Closes #13
2021-03-28 11:10:39 -04:00
Jikoo 7ab86f2af9 Fix build 2021-03-22 12:20:58 -04:00
Jikoo 2b563e0e1b Don't convert release notes to HTML
We'll try this in another few months when there're enough changes to be worth it.
2021-03-22 12:07:53 -04:00
Jikoo 2a0e0d33d8 Don't shade annotations 2021-03-22 11:56:39 -04:00
Jikoo 889c2ffdab Bump version to 4.1.7-SNAPSHOT for development 2021-03-22 11:52:14 -04:00
Jikoo c440f618c9 Bump version to 4.1.6 for release 2021-03-22 11:52:04 -04:00
Jikoo ccc6f4b4a6 Fix CF release 2021-03-22 11:50:56 -04:00
Jikoo ae6c3bd292 Move release location 2021-03-22 10:24:48 -04:00
Jikoo 0e3bdb8741 Convert to HTML for CF 2021-03-22 10:21:09 -04:00
Jikoo e09e7c59c7 Fix release action 2021-03-22 09:42:52 -04:00
Jikoo 18c7916d79 Include release script 2021-03-22 09:41:36 -04:00
Jikoo 15ee6ef9a5 Fix revision number 2021-03-20 13:19:33 -04:00
Jikoo 24224e4f9d Add CurseForge release workflow 2021-03-20 13:06:33 -04:00
Jikoo 6563b4f6ce Migrate API-only functions out of PlayerDataManager
With the update to 1.16 there's no need to maintain multiple copies of the same code. Additionally, in 1.16 the action bar now supports JSON text.
2021-03-18 20:31:45 -04:00
Jikoo 1d5a836fd0 Partially automate release changelog 2021-03-18 16:38:41 -04:00
Jikoo 3ba82683ec Fix double chest custom names not displaying
Closes #175
2021-03-17 00:09:10 -04:00
Jikoo 6e5c332272 Extract player implementation 2021-03-16 23:49:17 -04:00
Jikoo 1c9d133ed1 Fix item delete in own inventory
Dragging items across top and bottom inventories with own inventory open resulted in the overlapping content being deleted.
2021-03-16 23:49:10 -04:00
Jikoo dad1e16c18 Fix item dupe in own inventory
Reduce desync for other actions in own inventory

Closes #182
2021-03-16 12:14:47 -04:00
Jikoo 519dd7da33 Fix cache breaking build on Spigot version change 2021-03-16 09:45:29 -04:00
Adam 1bd7932cc5 Drop 1.8/1.15/1.16.3 support (#185)
With the recent uptick in requests for support by people running 1.12 who have recently updated past Java 9, I revisited the supported versions.

First, the "modern" versions: 1.15 is run by 2.9% of servers, 1.16.3 by 5%. Since all of these versions are supported to ease the transition of updating servers and servers have updated, there's no real reason to keep them around. It's a lot easier if you can update a plugin and just have it work on both versions rather than push all the plugin updates to master with the server update, but none of these versions have any reason for long-term support.

Regarding heavily outdated server software: As of the time of writing, 1.8 and 1.12 have market shares of 8.6% and 8.5% of servers respectively.
Regarding 1.8: 1.8 support is already a bit wonky - with the changes made to inventory names, it's not (easily) possible to bridge the gap, and future changes will make that more and more difficult. People use 1.8 because they disliked the 1.9 combat changes, but there are plugins that fully rework combat to how it used to be. I have yet to hear a compelling argument that cannot be resolved with plugins. In the interest of my own sanity (handling and backporting the inventory name change in particular was a real humdinger) I will no longer be backporting changes to 1.8.
Regarding 1.12: I am honestly not sure why people are not updating. I get it, 1.13 and the flattening was a rough transition, but pretty much every developer active at the time bridged the gap. If your server is heavily dependent on some software only available at that time, you can live with other software available at that time. At the time of 1.12's release, Java 9 was not released. Either update your server, downgrade Java, or fork OpenInv and backport Java 9 support yourself.
2021-03-16 09:34:41 -04:00
Adam dad00d2d9c build: Add action to build and automatically release files (#170)
Auto-release is gonna get a nice live test at a later date.
2020-11-26 11:05:45 -05:00
Jikoo 502f661be3 fix: Update 1.15 adapter to 1.15.2 2020-11-26 10:47:51 -05:00
Jikoo 804d30bcc7 tpyo 2020-11-15 14:33:28 -05:00
Jikoo db8b67082b Fix up .gitignore a bit 2020-11-15 13:25:50 -05:00
Jikoo b2156edcc7 Fix mistake in InventoryAccess grabbing ender chest 2020-11-15 12:59:59 -05:00
Jikoo 45de495699 Simplify command executor assignment a little more 2020-11-15 12:26:36 -05:00
Jikoo f45d332727 Clarify download locations for older versions
Hopefully this will help people figure out where they've gone wrong. Maybe I should be stating supported versions in release notes too.
2020-11-15 12:22:25 -05:00
Jikoo 9705cb156b Bump version to 4.1.6-SNAPSHOT for development 2020-11-03 18:32:43 -05:00
Jikoo 1c45ef517f Bump version to 4.1.5 for release 2020-11-03 18:32:11 -05:00
Jikoo 3fee84fc58 Add support for 1.16.4, drop 1.16.1 2020-11-03 18:19:34 -05:00
Jikoo 00113cc4d4 These are my favorite version bumps 2020-09-15 13:47:12 -04:00
Jikoo 788f022e74 Mitigate some permissions confusion
For some reason, certain permissions plugins seem to not respect our declared default false nodes. To alleviate user confusion, these nodes will not be allowed to be used via wildcard internally.
2020-09-15 13:44:50 -04:00
Jikoo da55790fd2 Allow spectators to edit inventories
Requires permission OpenInv.spectate
Closes #155
2020-09-15 13:35:04 -04:00
Jikoo 1a6d513603 Reduce technical debt
* Reduce duplicate code
* Use more specific functional interfaces where available
* Fix some potential NPEs
* Remove some unnecessary/nonfunctional code
* Merge inventory listeners - no longer need to keep separate due to event availability
* Removed TODO items that probably won't ever be implemented. Good ideas, too drastic changes or too much work to maintain.
2020-09-15 12:51:49 -04:00
Jikoo a5b02ab26a Bump version to 4.1.5-SNAPSHOT for development 2020-08-22 12:22:00 -04:00
Jikoo 0c3ce2dfb3 Bump version to 4.1.4 for release 2020-08-22 12:21:22 -04:00
Jikoo a1f4649a09 Reword invalid version message, include releases link 2020-08-22 12:00:08 -04:00
Jikoo 100d0e12cd Update to 1.16.2 2020-08-12 00:06:20 -04:00
Jikoo 3e629798e5 Bump version to 4.1.4-SNAPSHOT for development 2020-07-15 23:23:31 -04:00
Jikoo 7c1f15974c Bump version to 4.1.3 for release 2020-07-15 23:23:03 -04:00
mfnalex d992237766 Add support for 3rd party plugins firing PlayerInteractEvent (#146)
* Add support for InvUnload

* generalize 3rd party plugins' event check
2020-07-15 22:38:38 -04:00
Jikoo c93464e643 Fix double chest loot generation with AnyContainer (#142) 2020-07-08 20:44:15 -04:00
Jikoo 56afefc82b Bump version to 4.1.3-SNAPSHOT for development 2020-06-25 19:40:07 -04:00
Jikoo f0a66570d2 Bump version to 4.1.2 for release 2020-06-25 19:39:38 -04:00
Jikoo 201a3578bf Add 1.16 support, drop 1.14 (#141) 2020-06-25 19:38:38 -04:00
Nathaniel Freeman 1228fc2de2 es_ES translation 2020-06-24 07:12:20 -04:00
NotMyFault 2561e75ae5 Fix typo 2020-06-02 19:01:33 -04:00
N0tMyFaultOG 80661b9465 Add german translation 2020-06-02 17:59:31 -04:00
Jikoo 53701da34f Bump version to 4.1.2-SNAPSHOT for development 2020-05-29 10:32:32 -04:00
Jikoo 9cf4712813 Bump version to 4.1.1 for release 2020-05-29 10:32:03 -04:00
Jikoo abe8fccdb5 Document new nodes 2020-05-29 10:27:50 -04:00
Jikoo 4499bb5453 Don't break behavior on existing setups
Permission to use /openinv and /openender will grant online/offline access so that existing permissions configurations do not have to be redone. Users looking to implement the new permissions will need to deny them.
2020-05-29 10:22:40 -04:00
Phoenix616 5405b7d7b9 Add permission for online/offline opening (Resolves #129) 2020-05-29 10:08:26 -04:00
129fc5e32c631b5007cb2cd8d45d416002b42bab 34199d12a4 Add localization for Brazilian Portuguese 2020-04-30 17:42:10 -04:00
Jikoo 67131ab9f9 Save after adding new keys 2020-04-24 10:47:51 -04:00
Jikoo 723381cb2b Fix IndexOutOfBounds on no args
Closes #128
2020-04-22 12:07:19 -04:00
Jikoo 34e7252d77 Correct lang + replacement mistake with inventory names 2020-04-19 18:04:10 -04:00
Jikoo a1b2df74cc Save existing vehicles if we had to load player (#22) 2020-04-19 17:54:45 -04:00
Jikoo 7210f9153e Bump version to 4.1.1-SNAPSHOT for development 2020-04-18 08:05:09 -04:00
Jikoo dcdfac0d3b Bump version to 4.1.0 for release 2020-04-18 08:04:38 -04:00
Jikoo c7b4554a6c It's been over 3 years :)
The common module was designed to prevent the internal modules depending on the core plugin. With the introduction of localization, this overcomplication became ever more exacerbated.
Probably will play around a bit more to remove freshly introduced static abuse before release.
Closes #61
2020-03-24 21:01:59 -04:00
Jikoo c51acb4e72 Add basic /searchcontainer
May be subject to change as I (very slowly) work towards console support for commands.
Closes #113
2020-03-15 10:43:08 -04:00
Jikoo 23d41cd6c8 Simplify and expand tab completion 2020-03-15 09:50:51 -04:00
Jikoo 8bc389496b Update cache for Java 8 2020-03-15 09:49:11 -04:00
Jikoo 1cc36d08bd Modify players before other plugins access them
Closes #123
2020-03-11 09:17:17 -04:00
Jikoo c8938f451d Update copyright 2020-02-01 08:03:15 -05:00
Jikoo 4f2665fedc Improve any/silent container commands
* Merged duplicate code
* Added on/off parameters - closes #120
* Added tab completion for parameters
2020-02-01 08:00:03 -05:00
unixminecraft 7d93bb06ad Updated /sc check command to correctly display if SilentChest is active or not. 2020-01-14 22:40:01 -05:00
Jikoo 19c491411f Bump version to 4.0.9-SNAPSHOT for development 2019-12-12 14:37:03 -05:00
Jikoo 65c6eb2693 Bump version to 4.0.8 for release 2019-12-12 14:36:38 -05:00
Jikoo ab1f4ace8a Fix AnyContainer breaking modified ender chest sizes 2019-12-12 14:33:42 -05:00
Jikoo e7dc52bee4 Drop support for 1.13, latest 2 versions only. 2019-12-12 14:21:33 -05:00
Jikoo a9c5799677 Merge branch 'master' of https://github.com/lishid/OpenInv 2019-12-12 13:59:38 -05:00
Andre LeBlanc 6d6257720a add support for 1.15 2019-12-12 13:59:12 -05:00
Andre LeBlanc 3e235fa688 add support for 1.15 2019-12-12 12:49:21 -05:00
Jikoo dd02355ac2 Properly remove SilentContainer viewers
Closes #115
2019-12-03 13:50:27 -05:00
Jikoo 4d3f5d67a8 Update copyright
Practically should make it 2020 at this rate, whoops

(cherry picked from commit ff76f2b95ac5ea2a32a42b5dcd82a5e5b762b831)
2019-12-03 13:32:41 -05:00
Jikoo 7ad5faa2ce Clean up maven instructions
(cherry picked from commit b92543e0789518183cfa685302d7938932b09550)
2019-12-03 13:32:37 -05:00
Jikoo c28383ef36 Work around 1.14 tab completion issues 2019-10-01 08:30:28 -04:00
Jikoo 5fbab1ab8f Bump version to 4.0.8-SNAPSHOT for development 2019-09-29 19:43:14 -04:00
Jikoo d97b092978 Bump version to 4.0.7 for release 2019-09-29 19:42:59 -04:00
Jikoo 154bf56c2a Include api-version in plugin.yml
Closes #111, #112
2019-09-29 19:40:06 -04:00
William Blake Galbreath d65a9caeb2 Add support for varying sizes of inventories 2019-08-01 21:04:23 -04:00
Jikoo b3c4253606 Bump version to 4.0.7-SNAPSHOT for development 2019-07-21 13:30:02 -04:00
Jikoo b64000e89b Bump version to 4.0.6 for release 2019-07-21 13:29:45 -04:00
Jikoo 3faf41ef18 More clear error when loot is not generated 2019-07-19 18:16:36 -04:00
Jikoo db27d25a93 More Bukkit API, less breaking with new versions 2019-07-19 18:14:14 -04:00
Jikoo 0f02e6fe66 Right, 1.13 is a thing 2019-07-19 18:12:06 -04:00
Jikoo ec07637e9f Bump version to 4.0.6-SNAPSHOT for development 2019-07-19 16:26:24 -04:00
Jikoo b269f3509c Bump version to 4.0.5 for release 2019-07-19 16:26:03 -04:00
Jikoo f2e4c8a3ad Fix barrel triggering AnyContainer (#108) 2019-07-19 16:24:58 -04:00
Jikoo 35839f1548 Bump version to 4.0.5-SNAPSHOT for development 2019-07-19 07:21:37 -04:00
Jikoo a4a462a1fa Bump version to 4.0.4 for release 2019-07-19 07:21:16 -04:00
Jikoo b9f901af66 Add silent barrels - fixes #106 2019-07-19 07:18:23 -04:00
Jikoo 1333056613 Correct item order - fixes #107 2019-07-19 07:13:11 -04:00
Jikoo fb23df313a Bump version to 4.0.4-SNAPSHOT for development 2019-05-27 20:53:44 -04:00
Jikoo 96dc400c83 Bump version to 4.0.3 for release 2019-05-27 20:53:02 -04:00
Jikoo b801d36f12 Turns out api-version below 1.13 is unsupported.
Compiling for 1.8, no api-version.
2019-05-27 20:52:49 -04:00
Jikoo 4c27599dd9 Bump version to 4.0.3-SNAPSHOT for development 2019-05-27 20:23:16 -04:00
Jikoo 64ad0a5267 Bump version to 4.0.2 for release 2019-05-27 20:22:50 -04:00
Jikoo cba7f44192 Include api-version in plugin.yml (Closes #102) 2019-05-27 20:22:02 -04:00
Jikoo e8ace6bd06 Update to 1.14.2 2019-05-27 20:12:39 -04:00
Jikoo fd70f260ba Fix mistake in string comparison utility 2019-05-27 20:05:58 -04:00
Jikoo be9874ba1d Fix pom changes causing Spigot API to be shaded into final jar 2019-05-27 20:05:06 -04:00
Jikoo 096faef161 Bump version to 4.0.2-SNAPSHOT for development 2019-05-15 19:20:50 -04:00
Jikoo df96d5106a Bump version to 4.0.1 for release 2019-05-15 19:20:25 -04:00
Jikoo fe77a1d2e7 Drop support for 1.14, add support for 1.14.1 2019-05-15 19:19:40 -04:00
Jikoo 3096e43540 Update to Java 8 and Minecraft 1.8.8 2019-05-05 22:20:43 -04:00
Jikoo 2939551d65 Delete modules for versions no longer being supported 2019-05-05 18:03:22 -04:00
Jikoo fb74fd3ced Bump version to 4.0.1-SNAPSHOT for development 2019-05-05 16:55:36 -04:00
Jikoo 393fe1d837 Bump version to 4.0.0 for release 2019-05-05 16:55:12 -04:00
Jikoo f80df9d83b Copy pasted javadocs are super great 2019-05-05 16:49:41 -04:00
Jikoo 5c006f8ebd QOL: Warn users when plugin is running on an unsupported version 2019-05-05 15:43:41 -04:00
Jikoo 57097e626e Reduce duplicate code in core plugin 2019-05-05 15:39:54 -04:00
Jikoo 0f266c8a64 Custom inventory titles for 1.14 2019-05-05 15:15:17 -04:00
Jikoo 185f037254 Revert API changes, fix SpecialPlayerInventory implementation 2019-05-05 12:40:47 -04:00
Jikoo e4d0020296 Update to 1.14 (#101) 2019-04-28 20:37:45 -04:00
Jikoo 9e37cbbca8 Prepare for 1.14
To continue providing renamed ender chests/player inventories we can no longer just provide an Inventory, a full InventoryView is required. To avoid confusion the old method has been removed entirely, leading to a major API revision bump.
2019-04-28 20:37:05 -04:00
Jikoo adc35e9ad5 Correct players not being properly released 2019-04-28 12:11:59 -04:00
Jikoo a469c732cc Store UUID instead of name for faster re-open lookups 2019-04-28 11:03:00 -04:00
Jikoo c5c1f47167 Hacky workaround for Java 9 reflection changes (#93) 2019-04-12 20:56:05 -04:00
Jikoo dc59356f76 Bump version to 3.3.8-SNAPSHOT for development 2019-01-18 18:36:28 -05:00
Jikoo d497772ad7 Bump version to 3.3.7 for release 2019-01-18 18:35:50 -05:00
Jikoo 4f4d46974a Support 1.13.2 (#94) 2019-01-15 20:01:41 -05:00
Jikoo 96c38895e2 Bump version to 3.3.7-SNAPSHOT for development 2018-08-26 14:47:13 -04:00
Jikoo 5753648d77 Bump version to 3.3.6 for release 2018-08-26 14:46:33 -04:00
Jikoo 3725711b94 Support Spigot 1.13.1 2018-08-26 14:43:21 -04:00
Jikoo c22e18c096 Correct unnecessary AnyContainer usage 2018-08-26 14:37:29 -04:00
Jikoo 6bff759d72 Bump version to 3.3.6-SNAPSHOT for development 2018-08-13 21:31:46 -04:00
Jikoo 582c7201dd Bump version to 3.3.5 for release 2018-08-13 21:31:17 -04:00
Jikoo 59a8ed70bb Correct double chest logic for Silent/AnyContainer in 1.13 2018-08-09 18:50:24 -04:00
Jikoo 27812c133d Bump version to 3.3.5-SNAPSHOT for development 2018-07-22 09:45:01 -04:00
Jikoo 165f3fb00e Bump version to 3.3.4 for release 2018-07-22 09:44:27 -04:00
Jikoo 60dcb64558 Bump version for alpha release 2018-07-15 12:05:52 -04:00
Jikoo 077b34d304 Support 1.13-pre7 2018-07-15 12:01:45 -04:00
Jikoo 8ce6e2f8d3 Reverse order of wildcard permission check, respect explicit values (#85) 2018-05-26 06:58:56 -04:00
Jikoo f5dce23f18 Bump version to 3.3.4-SNAPSHOT for development 2018-02-07 18:36:15 -05:00
Jikoo 669cfd72aa Bump version to 3.3.3 for release 2018-02-07 18:35:39 -05:00
Jikoo 9e66885690 First load should not "update" nonexistent config, just use a default 2018-02-07 18:17:33 -05:00
Jikoo a802769265 Sneaky tabs, trying to slip in there. 2018-02-04 10:35:35 -05:00
Jikoo 50c45b6616 Cleaned up code, updated copyright at long last. 2018-02-04 10:29:09 -05:00
Jikoo eaf01fa32c Don't cache simultaneously added players forever 2017-11-19 14:46:52 -05:00
Jikoo 4abaa6ea51 Backport spectate abuse to 1.8-1.10 to support PaperSpigot
This may break functionality on C(K)auldron and other server implementations with different remapping names.
2017-11-08 19:07:23 -05:00
Jikoo 7c621bf899 Bump version to 3.3.3-SNAPSHOT for development 2017-11-08 17:53:16 -05:00
Jikoo f5c682ff2b Bump version to 3.3.2 for release 2017-11-08 17:52:41 -05:00
Jikoo e652b43670 Classic mistake, changed my mind and only changed part of the code.
Also the actual mistake. Never forget. (#81)
2017-11-08 17:40:01 -05:00
Jikoo f07cef5621 Bump version to 3.3.2-SNAPSHOT for development 2017-11-07 18:18:33 -05:00
Jikoo ded6421e33 Bump version to 3.3.1 for release 2017-11-07 18:17:12 -05:00
Jikoo cbb2b4d232 Lowercase permissions can wait for a major version bump
I don't want to double our permissions checks for backwards compatibility.
2017-11-07 18:16:38 -05:00
Jikoo 48c61eeb82 Support lowercase permissions for consistency. 2017-11-06 19:23:26 -05:00
Jikoo e3d1af010b Add permissions to alter the default state of Any/SilentChest (#76) 2017-11-05 20:00:22 -05:00
Jikoo 2195677651 Actually fix CME (#71) 2017-08-06 16:35:18 -04:00
Jikoo c72af5dbac Bump version to 3.3.1-SNAPSHOT for development 2017-07-05 05:53:38 -04:00
Jikoo 931cdb6fb1 Bump version to 3.3.0 for release 2017-07-05 05:52:33 -04:00
Jikoo 9db18613a1 Abuse spectator mode for SilentContainer (#64, #68)
Potentially causes issues with Cauldron and similar mods that use different mapping names.
2017-07-04 13:22:03 -04:00
Jikoo 784935a975 Synchronize cache (for real), updated dependencies
Closes #69
2017-07-03 18:06:18 -04:00
Jikoo d9abe76531 Bump version to 3.2.2-SNAPSHOT for development 2017-06-09 20:21:30 -04:00
Jikoo ca85a392aa Correct field name for list of inventory contents (#66) 2017-06-09 20:15:51 -04:00
Jikoo 307f47e4d3 Bump version to 3.2.1-SNAPSHOT for development 2017-06-08 18:44:10 -04:00
Jikoo 96c59f163d Slightly improve API, bump version to 3.2.0 for release
Methods are now properly annotated @Nullable when they may return null. More descriptive exceptions are thrown when issues occur instead of just returning null.
2017-06-08 18:36:01 -04:00
Jikoo 01f147b13c Fix incorrect method call for checking if a block is occluding
Switched to using the API for statistic manipulation.
2017-06-08 18:26:26 -04:00
Jikoo 1c104b775f For improved compatibility, our own Levenshtein implementation 2017-05-28 09:53:59 -04:00
Jikoo 9a53e46dd3 Replaced modern profile with recent
Going forward, the primary file uploaded to Bukkit will probably be the smaller build made with the `recent` profile, supporting only the last 2 versions. The uberjar created by the `all` will be provided as an additional download. OpenInv v3.1.3-SNAPSHOT `all` uberjar currently is 276KB. The `recent` jar is only 81KB. It's getting silly, and will only continue to get worse.
2017-05-26 05:52:35 -04:00
Jikoo 6744e71127 Support 1.12-pre2 (#65) 2017-05-14 11:47:15 -04:00
Jikoo 12c3ebb798 Bump version to 3.1.3-SNAPSHOT for development 2017-02-13 20:02:12 -05:00
Jikoo 32ec8d8147 Bump version to 3.1.2 for release 2017-02-13 20:01:18 -05:00
Jikoo 443e0c489e Synchronize cache (#63) 2017-02-13 19:56:32 -05:00
Jikoo 7ab0003b62 Bump version to 3.1.2-SNAPSHOT for development 2017-01-30 13:38:36 -05:00
Jikoo 12c0cac311 Bump version to 3.1.1 for release 2017-01-30 13:38:10 -05:00
Jikoo f54481e872 Fix (NoSuchMethod)Exception being caught instead of NoSuchMethodError
Closes #62
2017-01-30 13:35:58 -05:00
Jikoo f27dd8a233 Bump version to 3.1.1-SNAPSHOT for development 2017-01-20 13:02:47 -05:00
Jikoo d6df1a0871 Bump version to 3.1.0 for release 2017-01-20 12:59:03 -05:00
Jikoo e34e51d127 Better support other Player implementations
Closes #60
Removed all volatile comments - they require me to occasionally manually maintain them and NMS/OBC imports are already kept separate
2017-01-20 12:56:31 -05:00
Jikoo 5ca2ae461a Properly use Maven versioning for better support of dependent plugins
Closes #59
Bumped version to 3.1.0-SNAPSHOT for development, really should have bumped the minor version a while back.
2017-01-17 18:06:35 -05:00
Jikoo 4095e8039d RIPo.bukkit.org
repo.bukkit.org is dead, Spigot doesn't host builds that old. Didn't realize that I'd installed it locally when compiling CB 1.4.5 to use with OpenInv. Whoops.

Corrected dependency order - no idea why this compiled at all before. My bad.
2017-01-15 08:08:51 -05:00
Jikoo 73f390ab20 Jitpack's Maven version is pretty outdated. Hopefully fixes build.
The `all` profile is no longer active by default, just in case.
2017-01-15 07:33:53 -05:00
Jikoo 2f7d3bc235 Call InventoryOpenEvent when using SilentChest
Backport fix for incorrect parameter as well, just in case.
Fixed 1.7 and lower forcing all chests' default names to be "Large chest" instead of the translatable string.
2017-01-10 14:12:58 -05:00
Jikoo ef486032f1 Fix incorrect parameter for SilentChest PacketPlayOutOpenWindow
Probably fixes issues with ProtocolSupport, and if it doesn't there's nothing else I can do as we'll be as close to identical to NMS as possible.
2017-01-10 12:50:24 -05:00
Jikoo 72ef873772 Fixes for shulker boxes and AnyChest in 1.11
Fixes IBlockData used being the default for a shulker box, closes #55
Corrected using wrong method to check if blocked in 1.11.2. Something something NMS version bumps.
2016-12-31 07:05:52 -05:00
Jikoo 9b6e32a775 Fix statistic changes breaking silent/anycontainer in 1.11.1+
C'mon Spigot team, this is why the revision bumps existed in the first place, to allow plugins that deal with NMS to easily and gracefully fail on unsupported versions. 1.11.1 should be R2, 1.11.2 should be R3. I get that NMS isn't officially supported, but honestly, either do away with the versioned packages entirely or keep up with them properly. It's a single string change, it's not like it's prohibitively difficult.
2016-12-29 09:05:01 -05:00
Jikoo f05110c9b8 Fix getOnlinePlayers call to support all versions
Moved IPlayerDataManager from api module to common module - it is not part of the API as there is no supported way to obtain an instance of it.
2016-12-22 09:55:51 -05:00
Jikoo a41f89b011 Release any players held by a disabling plugin 2016-12-16 15:13:04 -05:00
Jikoo d24827ffcb Allow plugins to indicate to OpenInv that a Player is in use
This allows API users to prevent issues caused by multiple different copies of the Player being loaded, such as #49. Multiple instances of the same player could be obtained by calling IOpenInv#loadPlayer, waiting for OpenInv to remove it from the cache, then calling the method again.
2016-12-14 19:49:18 -05:00
Jikoo 802ce28103 Fix issue caused by module cleanup while backporting awarding achievement 2016-12-05 19:02:43 -05:00
Jikoo f31356b227 Fix permissions mistakes in readme 2016-12-05 19:01:21 -05:00
Jikoo 7942466863 Maven cleanliness and API progress
The project was very messy and due to older Bukkit packaging conventions, 1_4_5 and 1_4_6 were sorted away from the rest of the versioned code. All of the versioned internals are now submodules of the internal module.
Rather than use the hackish existing method of abusing the shade plugin to combine "dependencies" for a dummy assembly project, we're actually using the assembly plugin.
Profiles are still split up between the parent pom and the internal module pom, but they're much more clean.

The API is now its own module and can be compiled and released as a separate file for developers. Soon, Bukkit ticket 20, you'll be closed.
2016-11-30 21:26:56 -05:00
Jikoo db2cade4e2 Don't convert names in config to UUIDs on the main thread
This is pretty messy, but I can't think of a better way to avoid saving about 5 times in a row. Then again, I did just wake up, so my brain may not be on point yet.
2016-11-27 17:30:39 -05:00
Jikoo 718b4bb5dd Added /searchenchant, fixed a couple little mistakes
Mistakes being breaking UUID-based lookups and /anychest's toggle.
MY BAD, SORRY.
2016-11-27 06:38:24 -05:00
Jikoo 6aa25dd2dc Don't use reflection to increment container counter
Fixes #53 (pretty please with a cherry on top)
2016-11-27 06:13:17 -05:00
Jikoo 0bbcf6cdb2 Strip removed config options, add DisableSaving and bump config version 2016-11-26 17:05:06 -05:00
ShadowRanger f11d60f78c Reimplement ShadowRanger's configuration updater and path changes
Bumped version for release
2016-11-26 16:57:03 -05:00
Jikoo 8a6b98614f Added support for UUID-based player lookups in 1.7.5+
You could argue that ShadowRanger's conversion of everything to UUID is better, but that would result in us having to contact Mojang's servers simply to fetch a player by UUID for versions < 1.7.5. It seems excessive (not to mention that uncached contact can result in rate limiting) when the server itself will not remember who they are across name changes. If they can re-obtain everything in their inventory, they can re-run /ac.
2016-11-26 15:31:53 -05:00
Jikoo d7eec528e4 Fix OpenInv/OpenEnder permissions logic a little bit
/openender should respect exempt and crossworld permissions
/openinv should allow opening of own inventory if exempt without override
2016-11-25 22:26:25 -05:00
Jikoo a10c61168a Add modules for 1.4.5 through 1.10, flesh out readme 2016-11-25 22:23:18 -05:00
Jikoo 5c4886c66b Reimplement /searchender 2016-11-25 16:59:41 -05:00
Jikoo 4335b8dc2c Update plugin to jikoo/master - numerous fixes and changes
* Added permissions to commands in plugin.yml
* Removed item wand functionality - see 3549431fbc for reasoning
* Changed a lot of player loading logic
* Added config option DisableSaving - see Jikoo#6
* Fixed closing SilentChest not dropping item on cursor
* Added SilentChest support for shulker boxes
2016-11-25 16:54:53 -05:00
Jikoo 3bf7225712 Convert to a multi-module Maven setup
This is much more user-friendly - users can either compile a specific module or create a profile to compile for the specific Minecraft versions they're looking to support. It means that it's much easier for people to continue assisting with the development of OpenInv in the future without access to every version of CB since 1.4.5.

This commit restores and updates most of the old system.
2016-11-25 16:10:40 -05:00
Jacob Martin b76440ab9a Update to 1.11 2016-11-20 19:25:02 -05:00
ShadowRanger 3152605bca Merge pull request #47 from ShadowRanger/master
Update to 1.10
2016-06-09 22:49:02 +10:00
ShadowRanger eaabd30dc2 Update to 1.10 2016-06-09 22:46:36 +10:00
ShadowRanger de0d28b12d Update to 1.10 2016-06-09 22:43:20 +10:00
ShadowRanger c3a8bc9486 Update to 1.10 2016-06-09 22:41:21 +10:00
ShadowRanger 5b77404597 Merge pull request #45 from ShadowRanger/master
Update to 1.9.4
2016-05-13 00:23:03 +10:00
ShadowRanger d9c1b00b50 Update to 1.9.4 2016-05-13 00:21:37 +10:00
ShadowRanger 2fc439b306 Update to 1.9.4 2016-05-13 00:20:24 +10:00
ShadowRanger 2cb1b01fbf Merge pull request #44 from Tim-Schneider/master
Update to 1.9.4
2016-05-12 13:49:37 +10:00
Tim-Schneider d16cca9847 fix for minecraft 1.9.4 2016-05-10 21:50:34 +02:00
ShadowRanger e39f009661 Merge pull request #43 from ShadowRanger/master
Implement Jikoo's data duplication glitch fix
2016-05-05 11:32:40 +10:00
ShadowRanger 1d622b653d Implement Jikoo's data duplication glitch fix 2016-05-05 11:32:01 +10:00
ShadowRanger 582a958195 Merge pull request #42 from ShadowRanger/master
Implement Jikoo's data duplication glitch fix
2016-05-05 11:25:28 +10:00
ShadowRanger 9245b4cc27 Implement Jikoo's data duplication glitch fix 2016-05-04 23:18:20 +10:00
ShadowRanger ba9396ad5c Implement Jikoo's data duplication glitch fix 2016-05-04 14:38:10 +10:00
ShadowRanger 27de16cba9 Merge pull request #39 from ShadowRanger/master
General refactoring
2016-04-12 14:38:37 +10:00
ShadowRanger 90ea073089 General refactoring 2016-04-12 14:34:46 +10:00
ShadowRanger 4811e53d12 General refactoring 2016-04-12 14:33:44 +10:00
ShadowRanger c7f38adb3f Implement cbarber's player data glitch and online/offline event changes 2016-04-12 14:25:09 +10:00
ShadowRanger 99a7359be3 General refactoring 2016-03-10 14:04:20 +11:00
ShadowRanger d42cc3e275 General refactoring 2016-03-10 14:01:56 +11:00
ShadowRanger b8f4589b87 General refactoring 2016-03-10 13:47:17 +11:00
ShadowRanger f9ac6804b2 General refactoring 2016-03-10 13:37:28 +11:00
ShadowRanger 62f5d54676 Merge pull request #38 from ShadowRanger/master
Added 1.9 shield slot support
2016-03-09 14:15:40 +11:00
ShadowRanger 8feca3fb21 Added 1.9 shield slot support 2016-03-09 14:14:27 +11:00
ShadowRanger 7b16140ee1 Merge pull request #37 from ShadowRanger/master
Update to CraftBukkit 1.9
2016-03-04 13:48:51 +11:00
ShadowRanger 14ec280d26 Updated to CraftBukkit 1.9 2016-03-04 13:46:29 +11:00
ShadowRanger 71ac05ae85 Updated to CraftBukkit 1.9 2016-03-04 13:45:54 +11:00
ShadowRanger c4a9cb0a4a Updated to CraftBukkit 1.9 2016-03-02 20:11:45 +11:00
ShadowRanger 7ae87d1dbe Updated to CraftBukkit 1.9 2016-03-02 13:46:42 +11:00
ShadowRanger 346036b7e7 Updated to CraftBukkit 1.9 2016-03-02 13:44:16 +11:00
ShadowRanger 69c355c8cd Merge pull request #33 from ShadowRanger/master
Fixed an error & updated Bukkit
2015-08-27 19:55:33 +10:00
ShadowRanger 8dbbe80dbf Fixed an error & updated Bukkit 2015-08-27 19:52:56 +10:00
ShadowRanger 77e6ce8c7c Merge pull request #32 from ShadowRanger/master
Minor change + version bump
2015-07-21 09:45:36 +10:00
ShadowRanger cd405a9b9e Minor fix/change 2015-07-21 09:43:06 +10:00
ShadowRanger b5d6cf047c Bump version for release 2015-07-21 09:29:34 +10:00
Lishid aaa9c4e190 Merge pull request #31 from LulzFTW/master
Fix openinv item status always being false
2015-07-20 12:24:21 -07:00
LulzFTW 6ea09dc964 Fix openinv item status always being false 2015-07-20 15:16:31 -04:00
ShadowRanger eace5ca8fb Merge pull request #30 from ShadowRanger/master
Bump version for release
2015-07-16 19:15:51 +10:00
ShadowRanger 03308be7f5 Bump version for release 2015-07-16 19:14:57 +10:00
Lishid 4f40459b96 Merge pull request #29 from ShadowRanger/master
Fix & improve UUID retrieval
2015-07-10 15:55:17 -07:00
ShadowRanger 791368b100 Fix & improve UUID retrieval 2015-07-10 19:44:45 +10:00
ShadowRanger 5dd3f5bbd7 Merge pull request #27 from ShadowRanger/master
Bug fixes, changes & improvements
2015-06-25 10:17:14 +10:00
ShadowRanger d1d60e55d5 More fixes and improvements - continued 2015-06-24 19:37:09 +10:00
ShadowRanger 471b631838 More fixes and improvements - continued 2015-06-24 19:19:45 +10:00
ShadowRanger 2f1fd87435 More fixes and improvements 2015-06-23 19:07:19 +10:00
ShadowRanger a03c73f8d6 More fixes and improvements 2015-06-23 19:05:26 +10:00
ShadowRanger f7029e5ee2 Another small change 2015-06-23 16:46:14 +10:00
ShadowRanger 802e405c4e Small change to update not required message 2015-06-23 16:28:58 +10:00
ShadowRanger 55deabe56b Small fixes 2015-06-23 16:17:51 +10:00
ShadowRanger f8e4e69e53 Bug fix and changes 2015-06-23 15:11:35 +10:00
ShadowRanger 3c7d1696ff Bug fix and changes 2015-06-23 13:35:25 +10:00
ShadowRanger 902def1dd7 Bug fix and changes 2015-06-23 13:31:26 +10:00
ShadowRanger f260ed1175 Merge pull request #26 from ShadowRanger/master
General refactoring + minor changes
2015-06-22 20:30:14 +10:00
ShadowRanger bc40e91215 Minor no permissions message change 2015-06-22 20:26:36 +10:00
ShadowRanger 120b35d394 General refactoring - revert mistake 2015-06-22 20:17:30 +10:00
ShadowRanger ded568ec3f General refactoring 2015-06-22 20:15:28 +10:00
Lishid e958059572 Merge pull request #25 from ShadowRanger/master
Maven, UUIDs, dropped support for old CB, and a bunch of good stuff.
2015-06-22 01:56:48 -07:00
ShadowRanger d988becf1f Migrate to UUID usage 2015-06-22 18:30:27 +10:00
ShadowRanger e8476d5b05 Migrate to UUID usage 2015-06-22 12:03:04 +10:00
Lishid 1ea0307156 Merge pull request #21 from Phoenix616/master
Asynchronous offline player lookup
2015-05-26 22:09:41 -07:00
Max Lee 4ae47b09dc Update to v1_8_R3 NMS (1.8.4 to 1.8.6) 2015-05-27 02:13:20 +02:00
Max Lee 3e9bb63894 We are working with tasks here. 2015-04-03 01:39:08 +02:00
Max Lee dce86d9c69 Made enderchests equally protected as inventories 2015-04-03 01:34:32 +02:00
Max Lee 4013098201 Add crossworld check to enderchests 2015-04-03 01:32:38 +02:00
Max Lee 56f633d58b world not work 2015-04-03 01:32:10 +02:00
Max Lee 6c1fb14cda Make inventory lookup of offline players asynchron 2015-04-03 01:30:42 +02:00
Max Lee ad0e55cf4c Fix armor 2015-04-03 00:44:09 +02:00
Max Lee c4fb5489fa Try to get the offline player directly 2015-04-03 00:29:51 +02:00
Lishid 3d3006c681 Merge pull request #19 from Hummer12007/master
Added CraftBukkit v1_8_R2 support
2015-03-14 18:09:48 -04:00
Hummer12007 a9d983ddc6 Added CraftBukkit v1_8_R2 support 2015-03-14 23:12:48 +02:00
Lishid f6554a2a51 Merge pull request #17 from Hummer12007/master
Fix trapped chests linking with ordinary chests, instead of trapped ones
2015-01-13 21:46:56 -05:00
Hummer12007 ec3426b827 Use the same method for getting BlockChest, when checking whether AnyChest is needed due to the side block as in d0494aa2 2015-01-13 21:55:16 +02:00
Hummer12007 d0494aa2e7 Fix trapped chests linking with ordinary chests, instead of trapped ones. 2015-01-13 21:48:27 +02:00
lishid 6adcd491db Added targets path to gitignore 2015-01-05 21:21:44 -05:00
lishid bc709336e1 Fixed silent chest issues 2015-01-05 21:15:35 -05:00
lishid 7d56a04283 Derp... 2014-12-31 16:26:54 -05:00
lishid 7851404d69 Fixed constructor not found issue. 2014-12-31 10:50:19 -05:00
lishid 30b5acc3b4 Removed duplicate file 2014-12-30 22:31:54 -05:00
lishid 29b0bc980f minor refactor + update internal chest logic 2014-12-30 22:28:52 -05:00
lishid b05279ed4b Moved to proper directories 2014-12-30 22:15:43 -05:00
Lishid 88f3cb0a3f Merge pull request #16 from Fysac/master
Add support for CB 1.8 R1
2014-12-01 11:46:07 -08:00
Fysac f48fdac085 Add support for CB 1.8 R1 2014-12-01 12:18:43 -05:00
339 changed files with 18279 additions and 14160 deletions

No files matched your search

+491
View File
@@ -0,0 +1,491 @@
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = false
max_line_length = 120
tab_width = 2
ij_continuation_indent_size = 4
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = true
ij_smart_tabs = false
ij_visual_guides =
ij_wrap_on_typing = false
[*.java]
ij_smart_tabs = true
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false
ij_java_align_multiline_annotation_parameters = false
ij_java_align_multiline_array_initializer_expression = false
ij_java_align_multiline_assignment = false
ij_java_align_multiline_binary_operation = false
ij_java_align_multiline_chained_methods = false
ij_java_align_multiline_deconstruction_list_components = true
ij_java_align_multiline_extends_list = false
ij_java_align_multiline_for = true
ij_java_align_multiline_method_parentheses = false
ij_java_align_multiline_parameters = false
ij_java_align_multiline_parameters_in_calls = false
ij_java_align_multiline_parenthesized_expression = false
ij_java_align_multiline_records = true
ij_java_align_multiline_resources = true
ij_java_align_multiline_ternary_operation = false
ij_java_align_multiline_text_blocks = false
ij_java_align_multiline_throws_list = false
ij_java_align_subsequent_simple_methods = false
ij_java_align_throws_keyword = false
ij_java_align_types_in_multi_catch = true
ij_java_annotation_parameter_wrap = off
ij_java_array_initializer_new_line_after_left_brace = false
ij_java_array_initializer_right_brace_on_new_line = true
ij_java_array_initializer_wrap = off
ij_java_assert_statement_colon_on_next_line = false
ij_java_assert_statement_wrap = off
ij_java_assignment_wrap = off
ij_java_binary_operation_sign_on_next_line = false
ij_java_binary_operation_wrap = off
ij_java_blank_lines_after_anonymous_class_header = 0
ij_java_blank_lines_after_class_header = 0
ij_java_blank_lines_after_imports = 1
ij_java_blank_lines_after_package = 1
ij_java_blank_lines_around_class = 1
ij_java_blank_lines_around_field = 0
ij_java_blank_lines_around_field_in_interface = 0
ij_java_blank_lines_around_field_with_annotations = 0
ij_java_blank_lines_around_initializer = 1
ij_java_blank_lines_around_method = 1
ij_java_blank_lines_around_method_in_interface = 1
ij_java_blank_lines_before_class_end = 1
ij_java_blank_lines_before_imports = 1
ij_java_blank_lines_before_method_body = 0
ij_java_blank_lines_before_package = 0
ij_java_block_brace_style = end_of_line
ij_java_block_comment_add_space = false
ij_java_block_comment_at_first_column = true
ij_java_builder_methods =
ij_java_call_parameters_new_line_after_left_paren = false
ij_java_call_parameters_right_paren_on_new_line = true
ij_java_call_parameters_wrap = off
ij_java_case_statement_on_separate_line = true
ij_java_catch_on_new_line = false
ij_java_class_annotation_wrap = split_into_lines
ij_java_class_brace_style = end_of_line
ij_java_class_count_to_use_import_on_demand = 1000
ij_java_class_names_in_javadoc = 1
ij_java_deconstruction_list_wrap = normal
ij_java_do_not_indent_top_level_class_members = false
ij_java_do_not_wrap_after_single_annotation = false
ij_java_do_not_wrap_after_single_annotation_in_parameter = false
ij_java_do_while_brace_force = never
ij_java_doc_add_blank_line_after_description = true
ij_java_doc_add_blank_line_after_param_comments = false
ij_java_doc_add_blank_line_after_return = false
ij_java_doc_add_p_tag_on_empty_lines = true
ij_java_doc_align_exception_comments = false
ij_java_doc_align_param_comments = false
ij_java_doc_do_not_wrap_if_one_line = false
ij_java_doc_enable_formatting = true
ij_java_doc_enable_leading_asterisks = true
ij_java_doc_indent_on_continuation = false
ij_java_doc_keep_empty_lines = true
ij_java_doc_keep_empty_parameter_tag = true
ij_java_doc_keep_empty_return_tag = true
ij_java_doc_keep_empty_throws_tag = true
ij_java_doc_keep_invalid_tags = true
ij_java_doc_param_description_on_new_line = false
ij_java_doc_preserve_line_breaks = false
ij_java_doc_use_throws_not_exception_tag = true
ij_java_else_on_new_line = false
ij_java_enum_constants_wrap = off
ij_java_enum_field_annotation_wrap = off
ij_java_extends_keyword_wrap = off
ij_java_extends_list_wrap = off
ij_java_field_annotation_wrap = split_into_lines
ij_java_field_name_prefix =
ij_java_field_name_suffix =
ij_java_finally_on_new_line = false
ij_java_for_brace_force = never
ij_java_for_statement_new_line_after_left_paren = false
ij_java_for_statement_right_paren_on_new_line = true
ij_java_for_statement_wrap = off
ij_java_generate_final_locals = false
ij_java_generate_final_parameters = false
ij_java_generate_use_type_annotation_before_type = true
ij_java_if_brace_force = never
ij_java_imports_layout = @*,*,|,java.**,|,$*
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
ij_java_keep_blank_lines_before_right_brace = 2
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
ij_java_keep_blank_lines_in_code = 2
ij_java_keep_blank_lines_in_declarations = 2
ij_java_keep_builder_methods_indents = false
ij_java_keep_control_statement_in_one_line = true
ij_java_keep_first_column_comment = true
ij_java_keep_indents_on_empty_lines = false
ij_java_keep_line_breaks = true
ij_java_keep_multiple_expressions_in_one_line = false
ij_java_keep_simple_blocks_in_one_line = false
ij_java_keep_simple_classes_in_one_line = true
ij_java_keep_simple_lambdas_in_one_line = false
ij_java_keep_simple_methods_in_one_line = false
ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_on_demand_import_from_same_package_first = true
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = false
ij_java_line_comment_add_space_on_reformat = false
ij_java_line_comment_at_first_column = true
ij_java_local_variable_name_prefix =
ij_java_local_variable_name_suffix =
ij_java_method_annotation_wrap = split_into_lines
ij_java_method_brace_style = end_of_line
ij_java_method_call_chain_wrap = off
ij_java_method_parameters_new_line_after_left_paren = true
ij_java_method_parameters_right_paren_on_new_line = true
ij_java_method_parameters_wrap = on_every_item
ij_java_modifier_list_wrap = false
ij_java_multi_catch_types_wrap = normal
ij_java_names_count_to_use_import_on_demand = 10
ij_java_new_line_after_lparen_in_annotation = false
ij_java_new_line_after_lparen_in_deconstruction_pattern = true
ij_java_new_line_after_lparen_in_record_header = false
ij_java_new_line_when_body_is_presented = false
ij_java_packages_to_use_import_on_demand =
ij_java_parameter_annotation_wrap = off
ij_java_parameter_name_prefix =
ij_java_parameter_name_suffix =
ij_java_parentheses_expression_new_line_after_left_paren = false
ij_java_parentheses_expression_right_paren_on_new_line = true
ij_java_place_assignment_sign_on_next_line = false
ij_java_prefer_longer_names = true
ij_java_prefer_parameters_wrap = false
ij_java_preserve_module_imports = true
ij_java_record_components_wrap = normal
ij_java_repeat_synchronized = true
ij_java_replace_instanceof_and_cast = true
ij_java_replace_null_check = true
ij_java_replace_sum_lambda_with_method_ref = true
ij_java_resource_list_new_line_after_left_paren = false
ij_java_resource_list_right_paren_on_new_line = true
ij_java_resource_list_wrap = off
ij_java_rparen_on_new_line_in_annotation = true
ij_java_rparen_on_new_line_in_deconstruction_pattern = true
ij_java_rparen_on_new_line_in_record_header = true
ij_java_space_after_closing_angle_bracket_in_type_argument = false
ij_java_space_after_colon = true
ij_java_space_after_comma = true
ij_java_space_after_comma_in_type_arguments = true
ij_java_space_after_for_semicolon = true
ij_java_space_after_quest = true
ij_java_space_after_type_cast = true
ij_java_space_before_annotation_array_initializer_left_brace = false
ij_java_space_before_annotation_parameter_list = false
ij_java_space_before_array_initializer_left_brace = false
ij_java_space_before_catch_keyword = true
ij_java_space_before_catch_left_brace = true
ij_java_space_before_catch_parentheses = true
ij_java_space_before_class_left_brace = true
ij_java_space_before_colon = true
ij_java_space_before_colon_in_foreach = true
ij_java_space_before_comma = false
ij_java_space_before_deconstruction_list = false
ij_java_space_before_do_left_brace = true
ij_java_space_before_else_keyword = true
ij_java_space_before_else_left_brace = true
ij_java_space_before_finally_keyword = true
ij_java_space_before_finally_left_brace = true
ij_java_space_before_for_left_brace = true
ij_java_space_before_for_parentheses = true
ij_java_space_before_for_semicolon = false
ij_java_space_before_if_left_brace = true
ij_java_space_before_if_parentheses = true
ij_java_space_before_method_call_parentheses = false
ij_java_space_before_method_left_brace = true
ij_java_space_before_method_parentheses = false
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
ij_java_space_before_quest = true
ij_java_space_before_switch_left_brace = true
ij_java_space_before_switch_parentheses = true
ij_java_space_before_synchronized_left_brace = true
ij_java_space_before_synchronized_parentheses = true
ij_java_space_before_try_left_brace = true
ij_java_space_before_try_parentheses = true
ij_java_space_before_type_parameter_list = false
ij_java_space_before_while_keyword = true
ij_java_space_before_while_left_brace = true
ij_java_space_before_while_parentheses = true
ij_java_space_inside_one_line_enum_braces = false
ij_java_space_within_empty_array_initializer_braces = false
ij_java_space_within_empty_method_call_parentheses = false
ij_java_space_within_empty_method_parentheses = false
ij_java_spaces_around_additive_operators = true
ij_java_spaces_around_annotation_eq = true
ij_java_spaces_around_assignment_operators = true
ij_java_spaces_around_bitwise_operators = true
ij_java_spaces_around_equality_operators = true
ij_java_spaces_around_lambda_arrow = true
ij_java_spaces_around_logical_operators = true
ij_java_spaces_around_method_ref_dbl_colon = false
ij_java_spaces_around_multiplicative_operators = true
ij_java_spaces_around_relational_operators = true
ij_java_spaces_around_shift_operators = true
ij_java_spaces_around_type_bounds_in_type_parameters = true
ij_java_spaces_around_unary_operator = false
ij_java_spaces_inside_block_braces_when_body_is_present = false
ij_java_spaces_within_angle_brackets = false
ij_java_spaces_within_annotation_parentheses = false
ij_java_spaces_within_array_initializer_braces = false
ij_java_spaces_within_braces = false
ij_java_spaces_within_brackets = false
ij_java_spaces_within_cast_parentheses = false
ij_java_spaces_within_catch_parentheses = false
ij_java_spaces_within_deconstruction_list = false
ij_java_spaces_within_for_parentheses = false
ij_java_spaces_within_if_parentheses = false
ij_java_spaces_within_method_call_parentheses = false
ij_java_spaces_within_method_parentheses = false
ij_java_spaces_within_parentheses = false
ij_java_spaces_within_record_header = false
ij_java_spaces_within_switch_parentheses = false
ij_java_spaces_within_synchronized_parentheses = false
ij_java_spaces_within_try_parentheses = false
ij_java_spaces_within_while_parentheses = false
ij_java_special_else_if_treatment = true
ij_java_static_field_name_prefix =
ij_java_static_field_name_suffix =
ij_java_subclass_name_prefix =
ij_java_subclass_name_suffix = Impl
ij_java_switch_expressions_wrap = normal
ij_java_ternary_operation_signs_on_next_line = false
ij_java_ternary_operation_wrap = off
ij_java_test_name_prefix =
ij_java_test_name_suffix = Test
ij_java_throws_keyword_wrap = off
ij_java_throws_list_wrap = off
ij_java_use_external_annotations = false
ij_java_use_fq_class_names = false
ij_java_use_relative_indents = false
ij_java_use_single_class_imports = true
ij_java_variable_annotation_wrap = off
ij_java_visibility = public
ij_java_while_brace_force = never
ij_java_while_on_new_line = false
ij_java_wrap_comments = false
ij_java_wrap_first_method_in_call_chain = false
ij_java_wrap_long_lines = false
ij_java_wrap_semicolon_after_call_chain = false
[*.properties]
ij_properties_align_group_field_declarations = false
ij_properties_keep_blank_lines = false
ij_properties_key_value_delimiter = equals
ij_properties_spaces_around_key_value_delimiter = false
[.editorconfig]
ij_editorconfig_align_group_field_declarations = false
ij_editorconfig_space_after_colon = false
ij_editorconfig_space_after_comma = true
ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.jspx,*.pom,*.rng,*.tagx,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul}]
ij_xml_align_attributes = true
ij_xml_align_text = false
ij_xml_attribute_wrap = normal
ij_xml_block_comment_add_space = false
ij_xml_block_comment_at_first_column = true
ij_xml_keep_blank_lines = 2
ij_xml_keep_indents_on_empty_lines = false
ij_xml_keep_line_breaks = true
ij_xml_keep_line_breaks_in_text = true
ij_xml_keep_whitespaces = false
ij_xml_keep_whitespaces_around_cdata = preserve
ij_xml_keep_whitespaces_inside_cdata = false
ij_xml_line_comment_at_first_column = true
ij_xml_space_after_tag_name = false
ij_xml_space_around_equals_in_attribute = false
ij_xml_space_inside_empty_tag = false
ij_xml_text_wrap = normal
[{*.bash,*.sh,*.zsh}]
ij_shell_binary_ops_start_line = false
ij_shell_keep_column_alignment_padding = false
ij_shell_minify_program = false
ij_shell_redirect_followed_by_space = false
ij_shell_switch_cases_indented = false
ij_shell_use_unix_line_separator = true
[{*.kt,*.kts}]
ij_kotlin_align_in_columns_case_branch = false
ij_kotlin_align_multiline_binary_operation = false
ij_kotlin_align_multiline_extends_list = false
ij_kotlin_align_multiline_method_parentheses = false
ij_kotlin_align_multiline_parameters = true
ij_kotlin_align_multiline_parameters_in_calls = false
ij_kotlin_allow_trailing_comma = false
ij_kotlin_allow_trailing_comma_on_call_site = false
ij_kotlin_assignment_wrap = normal
ij_kotlin_blank_lines_after_class_header = 0
ij_kotlin_blank_lines_around_block_when_branches = 0
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
ij_kotlin_block_comment_add_space = false
ij_kotlin_block_comment_at_first_column = true
ij_kotlin_call_parameters_new_line_after_left_paren = true
ij_kotlin_call_parameters_right_paren_on_new_line = true
ij_kotlin_call_parameters_wrap = on_every_item
ij_kotlin_catch_on_new_line = false
ij_kotlin_class_annotation_wrap = split_into_lines
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
ij_kotlin_continuation_indent_for_chained_calls = false
ij_kotlin_continuation_indent_for_expression_bodies = false
ij_kotlin_continuation_indent_in_argument_lists = false
ij_kotlin_continuation_indent_in_elvis = false
ij_kotlin_continuation_indent_in_if_conditions = false
ij_kotlin_continuation_indent_in_parameter_lists = false
ij_kotlin_continuation_indent_in_supertype_lists = false
ij_kotlin_else_on_new_line = false
ij_kotlin_enum_constants_wrap = off
ij_kotlin_extends_list_wrap = normal
ij_kotlin_field_annotation_wrap = split_into_lines
ij_kotlin_finally_on_new_line = false
ij_kotlin_if_rparen_on_new_line = true
ij_kotlin_import_nested_classes = false
ij_kotlin_imports_layout = *,java.**,javax.**,kotlin.**,^
ij_kotlin_indent_before_arrow_on_new_line = true
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
ij_kotlin_keep_blank_lines_before_right_brace = 2
ij_kotlin_keep_blank_lines_in_code = 2
ij_kotlin_keep_blank_lines_in_declarations = 2
ij_kotlin_keep_first_column_comment = true
ij_kotlin_keep_indents_on_empty_lines = false
ij_kotlin_keep_line_breaks = true
ij_kotlin_lbrace_on_next_line = false
ij_kotlin_line_break_after_multiline_when_entry = true
ij_kotlin_line_comment_add_space = false
ij_kotlin_line_comment_add_space_on_reformat = false
ij_kotlin_line_comment_at_first_column = true
ij_kotlin_method_annotation_wrap = split_into_lines
ij_kotlin_method_call_chain_wrap = normal
ij_kotlin_method_parameters_new_line_after_left_paren = true
ij_kotlin_method_parameters_right_paren_on_new_line = true
ij_kotlin_method_parameters_wrap = on_every_item
ij_kotlin_name_count_to_use_star_import = 5
ij_kotlin_name_count_to_use_star_import_for_members = 3
ij_kotlin_packages_to_use_import_on_demand = java.util.*,kotlinx.android.synthetic.**,io.ktor.**
ij_kotlin_parameter_annotation_wrap = off
ij_kotlin_space_after_comma = true
ij_kotlin_space_after_extend_colon = true
ij_kotlin_space_after_type_colon = true
ij_kotlin_space_before_catch_parentheses = true
ij_kotlin_space_before_comma = false
ij_kotlin_space_before_extend_colon = true
ij_kotlin_space_before_for_parentheses = true
ij_kotlin_space_before_if_parentheses = true
ij_kotlin_space_before_lambda_arrow = true
ij_kotlin_space_before_type_colon = false
ij_kotlin_space_before_when_parentheses = true
ij_kotlin_space_before_while_parentheses = true
ij_kotlin_spaces_around_additive_operators = true
ij_kotlin_spaces_around_assignment_operators = true
ij_kotlin_spaces_around_elvis = true
ij_kotlin_spaces_around_equality_operators = true
ij_kotlin_spaces_around_function_type_arrow = true
ij_kotlin_spaces_around_logical_operators = true
ij_kotlin_spaces_around_multiplicative_operators = true
ij_kotlin_spaces_around_range = false
ij_kotlin_spaces_around_relational_operators = true
ij_kotlin_spaces_around_unary_operator = false
ij_kotlin_spaces_around_when_arrow = true
ij_kotlin_variable_annotation_wrap = off
ij_kotlin_while_on_new_line = false
ij_kotlin_wrap_elvis_expressions = 1
ij_kotlin_wrap_expression_body_functions = 1
ij_kotlin_wrap_first_method_in_call_chain = false
[{*.har,*.json,*.jsonc,*.mcmeta,.prettierrc}]
ij_json_array_wrapping = split_into_lines
ij_json_keep_blank_lines_in_code = 0
ij_json_keep_indents_on_empty_lines = false
ij_json_keep_line_breaks = true
ij_json_keep_trailing_comma = false
ij_json_object_wrapping = split_into_lines
ij_json_property_alignment = do_not_align
ij_json_space_after_colon = true
ij_json_space_after_comma = true
ij_json_space_before_colon = false
ij_json_space_before_comma = false
ij_json_spaces_within_braces = false
ij_json_spaces_within_brackets = false
ij_json_wrap_long_lines = false
[{*.htm,*.html,*.sht,*.shtm,*.shtml}]
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
ij_html_align_attributes = true
ij_html_align_text = false
ij_html_attribute_wrap = normal
ij_html_block_comment_add_space = false
ij_html_block_comment_at_first_column = true
ij_html_do_not_align_children_of_min_lines = 0
ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p
ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot
ij_html_enforce_quotes = false
ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
ij_html_keep_blank_lines = 2
ij_html_keep_indents_on_empty_lines = false
ij_html_keep_line_breaks = true
ij_html_keep_line_breaks_in_text = true
ij_html_keep_whitespaces = false
ij_html_keep_whitespaces_inside = span,pre,textarea
ij_html_line_comment_at_first_column = true
ij_html_new_line_after_last_attribute = never
ij_html_new_line_before_first_attribute = never
ij_html_quote_style = double
ij_html_remove_new_line_before_tags = br
ij_html_space_after_tag_name = false
ij_html_space_around_equality_in_attribute = false
ij_html_space_inside_empty_tag = false
ij_html_text_wrap = normal
[{*.markdown,*.md}]
ij_markdown_force_one_space_after_blockquote_symbol = true
ij_markdown_force_one_space_after_header_symbol = true
ij_markdown_force_one_space_after_list_bullet = true
ij_markdown_force_one_space_between_words = true
ij_markdown_format_tables = true
ij_markdown_insert_quote_arrows_on_wrap = true
ij_markdown_keep_indents_on_empty_lines = false
ij_markdown_keep_line_breaks_inside_text_blocks = true
ij_markdown_max_lines_around_block_elements = 1
ij_markdown_max_lines_around_header = 1
ij_markdown_max_lines_between_paragraphs = 1
ij_markdown_min_lines_around_block_elements = 1
ij_markdown_min_lines_around_header = 1
ij_markdown_min_lines_between_paragraphs = 1
ij_markdown_wrap_text_if_long = true
ij_markdown_wrap_text_inside_blockquotes = true
[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock,uv.lock}]
ij_toml_keep_indents_on_empty_lines = false
[{*.yaml,*.yml}]
ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false
ij_yaml_indent_sequence_value = true
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_line_comment_add_space = false
ij_yaml_line_comment_add_space_on_reformat = false
ij_yaml_line_comment_at_first_column = true
ij_yaml_sequence_on_new_line = false
ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true
+11
View File
@@ -0,0 +1,11 @@
# Use lf endings by default.
* text=auto eol=lf
# Declare text file types just in case
*.java text
*.yml text
*.xml text
*.md text
# Exclude binary files
*.png binary
+19
View File
@@ -0,0 +1,19 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "gradle"
directory: "/"
schedule:
interval: "monthly"
ignore:
# The SQLite JDBC is provided by Spigot and, for now, Paper.
# To ensure it is still present even if Paper stops providing it,
# we need to declare it as a library.
# To prevent needlessly downloading (and loading) a second copy,
# we want to ensure that we are using the provided version.
- dependency-name: "org.xerial:sqlite-jdbc"
# Spigot and Spigot API updates are manual.
- dependency-name: "org.spigotmc:*"
@@ -0,0 +1,55 @@
name: Auto-merge Dependabot PRs
on:
workflow_run:
workflows: [ "Pull Request" ]
types: [ completed ]
jobs:
merge-dependabot:
if: "github.actor == 'dependabot[bot]'
&& github.event.workflow_run.event == 'pull_request'
&& github.event.workflow_run.conclusion == 'success'"
runs-on: ubuntu-latest
steps:
# Note: this is directly from GitHub's example for using data from a triggering workflow:
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow
- name: 'Download artifact'
uses: actions/github-script@v9
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "pr_number"
})[0];
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/pr_number.zip`, Buffer.from(download.data));
# This might be a useless use of cat, but I'm not sure what shell Actions is going to be running.
- name: Add Pull Number Variable
run: |-
unzip pr_number.zip
echo "PR_NUMBER=$(cat pr_number)" >> "$GITHUB_ENV"
- name: Approve
uses: hmarr/auto-approve-action@v4.0.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
pull-request-number: "${{ env.PR_NUMBER }}"
- name: Merge
uses: pascalgn/automerge-action@v0.16.4
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
MERGE_LABELS: "dependencies,java"
MERGE_METHOD: "squash"
PULL_REQUEST: "${{ env.PR_NUMBER }}"
+36
View File
@@ -0,0 +1,36 @@
name: OpenInv CI
on:
push:
branches:
- 'master'
tags-ignore:
- '**'
paths-ignore:
- resource-pack/openinv-legibility-pack/**
# Enable running CI via other Actions, i.e. for drafting releases and handling PRs.
workflow_call:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
distribution: 'temurin'
java-version: '25'
- uses: gradle/actions/setup-gradle@v5
- name: Build with Gradle
run: ./gradlew build
# Upload artifacts
- name: Upload Distributable Jar
id: upload-final
uses: actions/upload-artifact@v7
with:
name: dist
path: ./dist/*
+38
View File
@@ -0,0 +1,38 @@
name: Draft Github Release
on:
push:
tags:
- '**'
jobs:
run-ci:
uses: Jikoo/OpenInv/.github/workflows/ci.yml@master
draft-release:
needs: [ run-ci ]
runs-on: ubuntu-latest
steps:
- name: Download build
uses: actions/download-artifact@v8
with:
name: dist
path: dist
- name: Create Release
id: create-release
uses: softprops/action-gh-release@v3.0.3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: "OpenInv ${{ github.ref_name }}"
body: |-
## Supported server versions
**Paper:** TODO VERSION, 26.1.2, 1.21.11, 1.21.10, 1.21.9, 1.21.8, 1.21.7, 1.21.6
**Spigot:** TODO VERSION
TODO HELLO HUMAN, PRESS THE GENERATE CHANGELOG BUTTON PLEASE.
Don't forget to add a newline after the resulting header and prettify the tag compare link!
draft: true
prerelease: false
files: ./dist/**
+110
View File
@@ -0,0 +1,110 @@
name: Release to external sites
on:
release:
types: [ released ]
jobs:
curseforge_release:
runs-on: ubuntu-latest
steps:
- &parse-paper-versions
name: Parse Paper Versions
id: parse_versions
run: |
# Find line declaring Paper versions.
raw=$(grep "\\*\\*Paper:\\*\\*" <<< "${{ github.event.release.body }}")
# Enable extended glob pattern to match 0 or more whitespace characters.
shopt -s extglob
# Trim Paper versions identifier prefix.
raw=${raw/*([[:space:]])'**'Paper:'**'*([[:space:]])/}
# Replace commas and optional spaces with a newline.
raw=${raw//,*([[:space:]])/$'\n'}
# Turn extglob back off.
shopt -u extglob
echo "$raw"
printf "PAPER_VERSIONS<<EOF\n%s\nEOF\n" "$raw" >> "$GITHUB_OUTPUT"
- &fetch-release-asset
name: Fetch Github Release Asset
uses: dsaltares/fetch-gh-release-asset@1.1.2
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: ${{ github.event.release.id }}
file: OpenInv.jar
- name: Create CurseForge Release
uses: Kira-NT/mc-publish@v3.3
with:
curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }}
curseforge-id: 31432
files: ./OpenInv.jar
game-versions: "${{ steps.parse_versions.outputs.PAPER_VERSIONS }}"
game-version-filter: releases
loaders: |
spigot
paper
folia
modrinth_release:
runs-on: ubuntu-latest
steps:
- *parse-paper-versions
- *fetch-release-asset
- name: Create Modrinth Release
uses: Kira-NT/mc-publish@v3.3
with:
modrinth-id: 1UlvXbzL
modrinth-featured: false
modrinth-unfeature-mode: none
modrinth-token: ${{ secrets.MODRINTH_TOKEN }}
files: ./OpenInv.jar
game-versions: "${{ steps.parse_versions.outputs.PAPER_VERSIONS }}"
game-version-filter: releases
loaders: |
spigot
paper
folia
hangar_release:
runs-on: ubuntu-latest
steps:
- name: Parse Hangar Paper Versions
id: parse_versions
run: |
# Find line declaring Paper versions.
raw=$(grep "\\*\\*Paper:\\*\\*" <<< "${{ github.event.release.body }}")
# Trim Paper versions identifier prefix.
raw="${raw/'**'Paper:'**'/}"
# Trim leading and trailing spaces.
raw="${raw##[[:space:]]}"
raw="${raw%%[[:space:]]}"
# Generate JSON for Hangar. GitHub Actions provides jq 1.7 in the 24.04 image.
# Split on comma and optional spaces.
hangar_versions=$(jq -nc --arg versions "$raw" '{"PAPER": $versions | split(", *";null)}')
echo "$hangar_versions"
echo "HANGAR_PAPER_VERSIONS=$hangar_versions" >> "$GITHUB_OUTPUT"
- *fetch-release-asset
- name: Create Hangar Release
uses: benwoo1110/hangar-upload-action@v1
with:
api_token: ${{ secrets.HANGAR_TOKEN }}
slug: 'OpenInv'
version: ${{ github.event.release.tag_name }}
channel: release
description: "${{ github.event.release.body }}"
files: |-
[
{
"path": "./OpenInv.jar",
"platforms": ["PAPER"]
}
]
platform_dependencies: "${{ steps.parse_versions.outputs.HANGAR_PAPER_VERSIONS }}"
+22
View File
@@ -0,0 +1,22 @@
name: Pull Request
on:
pull_request:
jobs:
run-ci:
uses: Jikoo/OpenInv/.github/workflows/ci.yml@master
store-dependabot-pr-data:
if: "github.actor == 'dependabot[bot]' && github.event_name == 'pull_request'"
runs-on: ubuntu-latest
steps:
# Note: this is directly from GitHub's example for using data from a triggering workflow:
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow
- name: Store Pull Number
run: |
mkdir -p ./pr
echo ${{ github.event.number }} > ./pr/pr_number
- uses: actions/upload-artifact@v7
with:
name: pr_number
path: pr/
+24
View File
@@ -0,0 +1,24 @@
name: Resource Pack CI
on:
push:
branches:
- 'master'
tags-ignore:
- '**'
paths:
- resource-pack/openinv-legibility-pack/**
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Build resource pack
id: upload-resource-pack
uses: actions/upload-artifact@v7
with:
name: openinv-legibility-pack
path: ./resource-pack/openinv-legibility-pack/
compression-level: 9
+14
View File
@@ -0,0 +1,14 @@
**/.settings
**/.project
**/.classpath
**/.idea/
**/.gradle/
**.iml
**/target/
**/bin/
**/out/
**/build/
**/dist/
**/dependency-reduced-pom.xml
**/pom.xml.versionsBackup
**/.vscode/
View File
File renamed without changes.
-27
View File
@@ -1,27 +0,0 @@
## Goals
This fork of OpenInv was created when the lag caused by offline player lookups post-UUID migration became too much to bear. Overall, it does not differ much from the original.
### Changes of Note
- Removed updater
- Removed wand
- New configuration option `DisableSaving`: Inventory edits will not be saved unless the user is online.
## Previous Versions
This fork supports any version after 1.4.5. Binaries are available in the [releases tab](https://github.com/Jikoo/OpenInv/releases). Please allow a brief period after the release of a new version for updates.
## License
```
Copyright (C) 2011-2014 lishid. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
```
+65
View File
@@ -0,0 +1,65 @@
## About
OpenInv is a Bukkit plugin which allows users to open and edit anyone's
inventory or ender chest - online or not!
Downloads are available on [GitHub](https://github.com/Jikoo/OpenInv/releases), [DBO](https://dev.bukkit.org/projects/openinv)/[CurseForge](https://www.curseforge.com/minecraft/bukkit-plugins/openinv), [Modrinth](https://modrinth.com/plugin/openinv), and [Hangar](https://hangar.papermc.io/jikoo/OpenInv).
## Features
- **OpenInv**: Open anyone's inventory, even if they're offline.
- Read-only mode! Don't grant edit permission.
- Cross-world support! Allow access only from the same world.
- No duplicate slots! Only armor is accessible when opening self (if allowed at all)!
- Drop items as the player! Place items in the dropper slot in the bottom right. Can be disabled via permission!
- Allow any item in armor slots! Configurable via permission.
- **OpenEnder**: Open anyone's ender chest, even if they're offline.
- Allow access only to own ender chest! Don't grant permission to open others.
- Read-only mode! Don't grant edit permission.
- Cross-world support! Allow access only from the same world.
- **SilentContainer**: Open containers without displaying an animation or making sound.
- **AnyContainer**: Open containers, even if blocked by ocelots or blocks.
- **Resource Pack Support**: Personalize view using resource pack tricks like custom fonts and characters. See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Customizing-Inventories) for details.
## Commands
See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Commands).
## Permissions
See [the wiki](https://github.com/Jikoo/OpenInv/wiki/Permissions)
## For Developers
### As a Dependency
The OpenInv API is available via [JitPack](https://jitpack.io/).
```xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
```
```xml
<dependencies>
<dependency>
<groupId>com.github.Jikoo</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
Execute the gradle wrapper:
`./gradlew build`
If you encounter issues with building the Spigot module, try running BuildTools manually.
+31
View File
@@ -0,0 +1,31 @@
repositories {
mavenCentral()
maven("https://hub.spigotmc.org/nexus/content/groups/public/")
}
dependencies {
compileOnly(libs.spigotapi)
implementation(project(":openinvapi"))
}
java {
toolchain.languageVersion = JavaLanguageVersion.of(21)
}
tasks.withType<JavaCompile>().configureEach {
options.release = 21
}
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"))
}
@@ -0,0 +1,152 @@
package com.github.jikoo.openinv.togglepersist;
import com.google.errorprone.annotations.Keep;
import com.lishid.openinv.event.PlayerToggledEvent;
import com.lishid.openinv.util.setting.PlayerToggle;
import com.lishid.openinv.util.setting.PlayerToggles;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
public class TogglePersist extends JavaPlugin implements Listener {
private final Map<UUID, Set<String>> enabledToggles = new HashMap<>();
@Override
public void onEnable() {
getServer().getPluginManager().registerEvents(this, this);
File file = new File(getDataFolder(), "toggles.yml");
// If there's no save file, there's nothing to load.
if (!file.exists()) {
return;
}
Configuration loaded = YamlConfiguration.loadConfiguration(file);
// For each toggle, enable loaded players.
for (String toggleName : loaded.getKeys(false)) {
PlayerToggle toggle = PlayerToggles.get(toggleName);
// Ensure toggle exists.
if (toggle == null) {
continue;
}
for (String idString : loaded.getStringList(toggleName)) {
// Ensure valid UUID.
UUID uuid;
try {
uuid = UUID.fromString(idString);
} catch (IllegalArgumentException e) {
continue;
}
// Track that toggle is enabled.
set(uuid, toggleName);
}
}
}
private void set(UUID playerId, String toggleName) {
enabledToggles.compute(
playerId,
(uuid, toggles) -> {
if (toggles == null) {
toggles = new HashSet<>();
}
toggles.add(toggleName);
return toggles;
}
);
}
@Override
public void onDisable() {
Map<String, List<String>> converted = getSaveData();
YamlConfiguration data = new YamlConfiguration();
for (Map.Entry<String, List<String>> playerToggle : converted.entrySet()) {
data.set(playerToggle.getKey(), playerToggle.getValue());
}
File file = new File(getDataFolder(), "toggles.yml");
try {
data.save(file);
} catch (IOException e) {
getLogger().log(Level.SEVERE, "Unable to save player toggle states", e);
}
}
private @NotNull Map<String, List<String>> getSaveData() {
Map<String, List<String>> converted = new HashMap<>();
for (Map.Entry<UUID, Set<String>> playerToggles : enabledToggles.entrySet()) {
String idString = playerToggles.getKey().toString();
for (String toggleName : playerToggles.getValue()) {
// Add player ID to listing for each enabled toggle.
converted.compute(
toggleName,
(name, ids) -> {
if (ids == null) {
ids = new ArrayList<>();
}
ids.add(idString);
return ids;
}
);
}
}
return converted;
}
@Keep
@EventHandler
private void onPlayerJoin(@NotNull PlayerJoinEvent event) {
UUID playerId = event.getPlayer().getUniqueId();
Set<String> toggleNames = enabledToggles.get(playerId);
if (toggleNames == null) {
return;
}
for (String toggleName : toggleNames) {
PlayerToggle toggle = PlayerToggles.get(toggleName);
if (toggle != null) {
toggle.set(playerId, true);
}
}
}
@Keep
@EventHandler
private void onToggleSet(@NotNull PlayerToggledEvent event) {
if (event.isEnabled()) {
set(event.getPlayerId(), event.getToggle().getName());
} else {
enabledToggles.computeIfPresent(
event.getPlayerId(),
(uuid, toggles) -> {
toggles.remove(event.getToggle().getName());
return toggles.isEmpty() ? null : toggles;
}
);
}
}
}
@@ -0,0 +1,7 @@
name: OITogglePersist
main: com.github.jikoo.openinv.togglepersist.TogglePersist
version: ${version}
author: Jikoo
description: An OpenInv addon allowing /anycontainer and /silentcontainer to persist across sessions.
api-version: "1.20"
depend: [ OpenInv ]
+30
View File
@@ -0,0 +1,30 @@
plugins {
`maven-publish`
}
repositories {
mavenCentral()
maven("https://hub.spigotmc.org/nexus/content/groups/public/")
}
dependencies {
compileOnly(libs.spigotapi)
}
java {
toolchain.languageVersion = JavaLanguageVersion.of(21)
}
tasks.withType<JavaCompile>().configureEach {
options.release = 21
}
publishing {
publications {
create<MavenPublication>("jitpack") {
groupId = "com.github.Jikoo.OpenInv"
artifactId = "openinvapi"
from(components["java"])
}
}
}
@@ -0,0 +1,199 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.UUID;
import java.util.logging.Logger;
/**
* Interface defining behavior for the OpenInv plugin.
*/
@NullMarked
public interface IOpenInv {
/**
* Check if the server version is supported by OpenInv.
*
* @return true if the server version is supported
*/
boolean isSupportedVersion();
/**
* Check the configuration value for whether OpenInv saves player data when unloading players. This is exclusively
* for users who do not allow editing of inventories, only viewing, and wish to prevent any possibility of bugs such
* as lishid#40. If true, OpenInv will not ever save any edits made to players.
*
* @return false unless configured otherwise
*/
boolean disableSaving();
/**
* Check the configuration value for whether OpenInv allows offline access. If true, OpenInv will not load or allow
* modification of players while they are not online. This does not prevent other plugins from using existing loaded
* players who have gone offline.
*
* @return false unless configured otherwise
* @since 4.2.0
*/
boolean disableOfflineAccess();
/**
* Check the configuration value for whether OpenInv uses history for opening commands. If false, OpenInv will use
* the previous parameterized search when no parameters are provided.
*
* @return false unless configured otherwise
* @since 4.3.0
*/
boolean noArgsOpensSelf();
/**
* Get the active {@link IAnySilentContainer} implementation.
*
* @return the active implementation for the server version
* @throws IllegalStateException if the server version is unsupported
*/
IAnySilentContainer getAnySilentContainer();
/**
* Get whether a user has AnyContainer mode enabled.
*
* @param offline the user to obtain the state of
* @return true if AnyContainer mode is enabled
*/
boolean getAnyContainerStatus(OfflinePlayer offline);
/**
* Set whether a user has AnyContainer mode enabled.
*
* @param offline the user to set the state of
* @param status the state of the mode
*/
void setAnyContainerStatus(OfflinePlayer offline, boolean status);
/**
* Get whether a user has SilentContainer mode enabled.
*
* @param offline the user to obtain the state of
* @return true if SilentContainer mode is enabled
*/
boolean getSilentContainerStatus(OfflinePlayer offline);
/**
* Set whether a user has SilentContainer mode enabled.
*
* @param offline the user to set the state of
* @param status the state of the mode
*/
void setSilentContainerStatus(OfflinePlayer offline, boolean status);
/**
* Get an {@link ISpecialEnderChest} for a user.
*
* @param player the {@link Player} owning the inventory
* @param online whether the owner is currently online
* @return the created inventory
* @throws IllegalStateException if the server version is unsupported
* @throws InstantiationException if there was an issue creating the inventory
*/
ISpecialEnderChest getSpecialEnderChest(
Player player,
boolean online
) throws InstantiationException;
/**
* Get an {@link ISpecialPlayerInventory} for a user.
*
* @param player the {@link Player} owning the inventory
* @param online whether the owner is currently online
* @return the created inventory
* @throws IllegalStateException if the server version is unsupported
* @throws InstantiationException if there was an issue creating the inventory
*/
ISpecialPlayerInventory getSpecialInventory(
Player player,
boolean online
) throws InstantiationException;
/**
* @deprecated Use {@link #openInventory(Player, ISpecialInventory, boolean)}
*/
@Deprecated(forRemoval = true, since = "5.2.0")
@Nullable InventoryView openInventory(Player player, ISpecialInventory inventory);
/**
* Open an {@link ISpecialInventory} for a {@link Player}.
*
* @param player the viewer
* @param inventory the inventory to open
* @param viewOnly whether the inventory should be view-only
* @return the resulting {@link InventoryView}
*/
@Nullable InventoryView openInventory(Player player, ISpecialInventory inventory, boolean viewOnly);
/**
* Check if a {@link Player} is currently loaded by OpenInv.
*
* @param playerUuid the {@link UUID} of the {@code Player}
* @return whether the {@code Player} is loaded
* @since 4.2.0
*/
boolean isPlayerLoaded(UUID playerUuid);
/**
* Load a {@link Player} from an {@link OfflinePlayer}. If the user has not played before or the default world for
* the server is not loaded, this will return {@code null}.
*
* @param offline the {@code OfflinePlayer} to load a {@code Player} for
* @return the loaded {@code Player}
* @throws IllegalStateException if the server version is unsupported
*/
@Nullable Player loadPlayer(final OfflinePlayer offline);
/**
* Match an existing {@link OfflinePlayer}. If the name is a {@link UUID#toString() UUID string}, this will only
* return the user if they have actually played on the server before, unlike {@link Bukkit#getOfflinePlayer(UUID)}.
*
* <p>This method is potentially very heavily blocking. It should not ever be called on the
* main thread, and if it is, a stack trace will be displayed alerting server owners to the
* call.
*
* @param name the string to match
* @return the user with the closest matching name
*/
@Nullable OfflinePlayer matchPlayer(String name);
/**
* Forcibly close inventories of and unload any cached data for a user.
*
* @param offline the {@link OfflinePlayer} to unload
*/
void unload(OfflinePlayer offline);
Logger getLogger();
}
@@ -0,0 +1,57 @@
package com.lishid.openinv.event;
import com.google.errorprone.annotations.RestrictedApi;
import com.lishid.openinv.internal.ISpecialInventory;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Event fired before OpenInv saves a player's data when closing an {@link ISpecialInventory}.
*/
@NullMarked
public class OpenPlayerSaveEvent extends PlayerSaveEvent {
private static final HandlerList HANDLERS = new HandlerList();
private final ISpecialInventory inventory;
/**
* Construct a new {@code OpenPlayerSaveEvent}.
*
* <p>The constructor is not considered part of the API, and may be subject to change.</p>
*
* @param player the player to be saved
* @param inventory the {@link ISpecialInventory} being closed
*/
@RestrictedApi(
explanation = "Constructor is not considered part of the API and may be subject to change.",
link = "",
allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java"
)
@ApiStatus.Internal
OpenPlayerSaveEvent(Player player, ISpecialInventory inventory) {
super(player);
this.inventory = inventory;
}
/**
* Get the {@link ISpecialInventory} that triggered the save by being closed.
*
* @return the special inventory
*/
public ISpecialInventory getInventory() {
return inventory;
}
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
}
@@ -0,0 +1,68 @@
package com.lishid.openinv.event;
import com.google.errorprone.annotations.RestrictedApi;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Event fired before a {@link Player} loaded via OpenInv is saved.
*/
@NullMarked
public class PlayerSaveEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLERS = new HandlerList();
private boolean cancelled = false;
/**
* Construct a new {@code PlayerSaveEvent}.
*
* <p>The constructor is not considered part of the API, and may be subject to change.</p>
*
* @param player the player to be saved
*/
@RestrictedApi(
explanation = "Constructor is not considered part of the API and may be subject to change.",
link = "",
allowedOnPath = ".*/com/lishid/openinv/event/(OpenPlayerSaveEvent|OpenEvents).java"
)
@ApiStatus.Internal
PlayerSaveEvent(Player player) {
super(player);
}
/**
* Get whether the event is cancelled.
*
* @return true if the event is cancelled
*/
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* Set whether the event is cancelled.
*
* @param cancel whether the event is cancelled
*/
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
}
@@ -0,0 +1,72 @@
package com.lishid.openinv.event;
import com.google.errorprone.annotations.RestrictedApi;
import com.lishid.openinv.util.setting.PlayerToggle;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import java.util.UUID;
/**
* Event fired after OpenInv modifies a toggleable setting for a player.
*/
@NullMarked
public class PlayerToggledEvent extends Event {
private static final HandlerList HANDLERS = new HandlerList();
private final PlayerToggle toggle;
private final UUID uuid;
private final boolean enabled;
@RestrictedApi(
explanation = "Constructor is not considered part of the API and may be subject to change.",
link = "",
allowedOnPath = ".*/com/lishid/openinv/event/OpenEvents.java"
)
@ApiStatus.Internal
PlayerToggledEvent(PlayerToggle toggle, UUID uuid, boolean enabled) {
this.toggle = toggle;
this.uuid = uuid;
this.enabled = enabled;
}
/**
* Get the {@link PlayerToggle} affected.
*
* @return the toggle
*/
public PlayerToggle getToggle() {
return toggle;
}
/**
* Get the {@link UUID} of the player whose setting was changed.
*
* @return the player ID
*/
public UUID getPlayerId() {
return uuid;
}
/**
* Get whether the toggle is enabled.
*
* @return true if the toggle is enabled
*/
public boolean isEnabled() {
return enabled;
}
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
}
@@ -0,0 +1,133 @@
/*
* Copyright (C) 2011-2023 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.EnderChest;
import org.bukkit.block.data.Directional;
import org.bukkit.entity.Cat;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.util.BoundingBox;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public interface IAnySilentContainer {
/**
* Forcibly open the container at the given coordinates for the Player. This will open blocked containers! Be sure
* to check {@link #isAnyContainerNeeded(Block)} first if that is not desirable.
*
* @param player the {@link Player} opening the container
* @param silent whether the container's noise is to be silenced
* @param block the {@link Block} of the container
* @return true if the container can be opened
*/
boolean activateContainer(Player player, boolean silent, Block block);
/**
* Perform operations required to close the current container silently.
*
* @param player the {@link Player} closing a container
*/
void deactivateContainer(Player player);
/**
* Check if the container at the given coordinates is blocked.
*
* @param block the {@link Block} of the container
* @return true if the container is blocked
*/
boolean isAnyContainerNeeded(Block block);
/**
* Check if a shulker box block cannot be opened under ordinary circumstances.
*
* @param shulkerBox the shulker box block
* @return whether the container is blocked
*/
default boolean isShulkerBlocked(Block shulkerBox) {
Directional directional = (Directional) shulkerBox.getBlockData();
BlockFace facing = directional.getFacing();
// Construct a new 1-block bounding box at the origin.
BoundingBox box = new BoundingBox(0, 0, 0, 1, 1, 1);
// Expand the box in the direction the shulker will open.
box.expand(facing, 0.5);
// Move the box away from the origin by a block so only the expansion intersects with a box around the origin.
box.shift(facing.getOppositeFace().getDirection());
// Check if the relative block's collision shape (which will be at the origin) intersects with the expanded box.
return shulkerBox.getRelative(facing).getCollisionShape().overlaps(box);
}
/**
* Check if a chest cannot be opened under ordinary circumstances.
*
* @param chest the chest block
* @return whether the container is blocked
*/
default boolean isChestBlocked(Block chest) {
org.bukkit.block.Block relative = chest.getRelative(0, 1, 0);
return relative.getType().isOccluding()
|| !chest.getWorld().getNearbyEntities(BoundingBox.of(relative), Cat.class::isInstance).isEmpty();
}
/**
* Check if the given {@link Block} is a container which can be unblocked or silenced.
*
* @param block the potential container
* @return true if the type is a supported container
*/
boolean isAnySilentContainer(Block block);
/**
* Check if the given {@link BlockState} is a container which can be unblocked or silenced.
*
* @param blockState the potential container
* @return true if the type is a supported container
*/
default boolean isAnySilentContainer(BlockState blockState) {
return (blockState instanceof InventoryHolder holder && isAnySilentContainer(holder))
|| blockState instanceof EnderChest;
}
/**
* Check if the given {@link InventoryHolder} is a container which can be unblocked or silenced.
*
* @param holder the potential container
* @return true if the type is a supported container
*/
default boolean isAnySilentContainer(@Nullable InventoryHolder holder) {
return holder instanceof org.bukkit.block.EnderChest
|| holder instanceof org.bukkit.block.Chest
|| holder instanceof org.bukkit.block.DoubleChest
|| holder instanceof org.bukkit.block.ShulkerBox
|| holder instanceof org.bukkit.block.Barrel;
}
/**
* Check if the given {@link Inventory} belongs to a container which can be unblocked or silenced.
*
* @param inventory the potential container inventory
* @return true if the type is a supported container
*/
boolean isAnySilentContainer(Inventory inventory);
}
@@ -1,25 +1,33 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* 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.
*
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.jspecify.annotations.NullMarked;
public interface IAnySilentChest {
public boolean isAnyChestNeeded(Player player, int x, int y, int z);
/**
* An {@link ISpecialInventory} representing an ender chest.
*/
@NullMarked
public interface ISpecialEnderChest extends ISpecialInventory {
@Override
default InventoryType getBukkitType() {
return InventoryType.ENDER_CHEST;
}
public boolean activateChest(Player player, boolean anychest, boolean silentchest, int x, int y, int z);
}
@@ -0,0 +1,76 @@
/*
* 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.jspecify.annotations.NullMarked;
/**
* Interface defining behavior for special inventories backed by other inventories' content listings.
*/
@NullMarked
public interface ISpecialInventory {
/**
* Get the {@link Inventory} associated with this {@code ISpecialInventory}.
*
* @return the Bukkit inventory
*/
Inventory getBukkitInventory();
/**
* Get the {@link InventoryType} corresponding to this {@code ISpecialInventory}.
*
* @return the type of Bukkit inventory
*/
InventoryType getBukkitType();
/**
* Set the owning {@link Player} instance to a newly-joined user.
*
* @param player the user coming online
*/
void setPlayerOnline(Player player);
/**
* Mark the owner of the inventory offline.
*
* @deprecated No longer used by implementations.
*/
@Deprecated(forRemoval = true, since = "5.1.11")
default void setPlayerOffline() {}
/**
* Get whether the inventory is being viewed by any users.
*
* @return true if the inventory is being viewed
*/
default boolean isInUse() {
return !getBukkitInventory().getViewers().isEmpty();
}
/**
* Get the {@link Player} who owns the inventory.
*
* @return the {@link HumanEntity} who owns the inventory
*/
HumanEntity getPlayer();
}
@@ -1,24 +1,33 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* 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.
*
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.Inventory;
import org.bukkit.event.inventory.InventoryType;
import org.jspecify.annotations.NullMarked;
/**
* An {@link ISpecialInventory} representing a player inventory.
*/
@NullMarked
public interface ISpecialPlayerInventory extends ISpecialInventory {
@Override
default InventoryType getBukkitType() {
return InventoryType.PLAYER;
}
public interface IInventoryAccess {
public boolean check(Inventory inventory, HumanEntity player);
}
@@ -0,0 +1,108 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import com.google.errorprone.annotations.RestrictedApi;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.function.BiFunction;
@NullMarked
public final class InventoryAccess {
private static @Nullable BiFunction<Inventory, Class<? extends ISpecialInventory>, @Nullable ISpecialInventory> provider;
public static boolean isUsable() {
return provider != null;
}
/**
* Check if an {@link Inventory} is an {@link ISpecialPlayerInventory} implementation.
*
* @param inventory the Bukkit inventory
* @return true if backed by the correct implementation
*/
public static boolean isPlayerInventory(Inventory inventory) {
return getPlayerInventory(inventory) != null;
}
/**
* Get the {@link ISpecialPlayerInventory} backing an {@link Inventory}. Returns {@code null} if the inventory is
* not backed by the correct class.
*
* @param inventory the Bukkit inventory
* @return the backing implementation if available
*/
public static @Nullable ISpecialPlayerInventory getPlayerInventory(Inventory inventory) {
return provider == null ? null : (ISpecialPlayerInventory) provider.apply(inventory, ISpecialPlayerInventory.class);
}
/**
* Check if an {@link Inventory} is an {@link ISpecialEnderChest} implementation.
*
* @param inventory the Bukkit inventory
* @return true if backed by the correct implementation
*/
public static boolean isEnderChest(Inventory inventory) {
return getEnderChest(inventory) != null;
}
/**
* Get the {@link ISpecialEnderChest} backing an {@link Inventory}. Returns {@code null} if the inventory is
* not backed by the correct class.
*
* @param inventory the Bukkit inventory
* @return the backing implementation if available
*/
public static @Nullable ISpecialEnderChest getEnderChest(Inventory inventory) {
return provider == null ? null : (ISpecialEnderChest) provider.apply(inventory, ISpecialEnderChest.class);
}
/**
* Get a {@link ISpecialInventory} backing an {@link Inventory}. Returns {@code null} if the inventory is not backed
* by the correct class.
*
* @param inventory the Bukkit inventory
* @return the backing implementation if available
*/
public static @Nullable ISpecialInventory getInventory(Inventory inventory) {
return provider == null ? null : provider.apply(inventory, ISpecialInventory.class);
}
@RestrictedApi(
explanation = "Not part of the API.",
link = "",
allowedOnPath = ".*/com/lishid/openinv/util/InternalAccessor.java"
)
@ApiStatus.Internal
static void setProvider(
@Nullable BiFunction<Inventory, Class<? extends ISpecialInventory>, @Nullable ISpecialInventory> provider
) {
InventoryAccess.provider = provider;
}
private InventoryAccess() {
throw new IllegalStateException("Cannot create instance of utility class.");
}
}
@@ -0,0 +1,129 @@
/*
* Copyright (C) 2011-2022 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import org.bukkit.permissions.Permissible;
import org.jspecify.annotations.NullMarked;
/**
* An enum containing all permissions directly checked by OpenInv.
*
* <p>Note that this is not an exhaustive list! This does not contain
* all permissions managed by Bukkit, largely parent nodes.</p>
*/
@NullMarked
public enum Permissions {
/// Permission to open one's own inventory.
INVENTORY_OPEN_SELF("inventory.open.self"),
/// Permission to open someone else's inventory.
INVENTORY_OPEN_OTHER("inventory.open.other"),
/**
* Permission to edit one's own inventory.
*
* <p>Note that this does not guarantee that the user has access to open the inventory!
* Be sure to check {@link #INVENTORY_OPEN_SELF} first.</p>
*/
INVENTORY_EDIT_SELF("inventory.edit.self"),
/**
* Permission to edit someone else's inventory.
*
* <p>Note that this does not guarantee that the user has access to open the inventory!
* Be sure to check {@link #INVENTORY_OPEN_OTHER} first.</p>
*/
INVENTORY_EDIT_OTHER("inventory.edit.other"),
/// Permission to insert any item into the head slot.
INVENTORY_SLOT_HEAD_ANY("inventory.slot.head.any"),
/// Permission to insert any item into the chest slot.
INVENTORY_SLOT_CHEST_ANY("inventory.slot.chest.any"),
/// Permission to insert any item into the legs slot.
INVENTORY_SLOT_LEGS_ANY("inventory.slot.legs.any"),
/// Permission to insert any item into the feet slot.
INVENTORY_SLOT_FEET_ANY("inventory.slot.feet.any"),
/// Permission to drop items as the player via the drop slot.
INVENTORY_SLOT_DROP("inventory.slot.drop"),
/// Permission to open one's own ender chest.
ENDERCHEST_OPEN_SELF("enderchest.open.self"),
/// Permission to open someone else's ender chest.
ENDERCHEST_OPEN_OTHER("enderchest.open.other"),
/**
* Permission to edit one's own ender chest.
*
* <p>Note that this does not guarantee that the user has access to open the inventory!
* Be sure to check {@link #ENDERCHEST_OPEN_SELF} first.</p>
*/
ENDERCHEST_EDIT_SELF("enderchest.edit.self"),
/**
* Permission to edit someone else's ender chest.
*
* <p>Note that this does not guarantee that the user has access to open the inventory!
* Be sure to check {@link #ENDERCHEST_OPEN_OTHER} first.</p>
*/
ENDERCHEST_EDIT_OTHER("enderchest.edit.other"),
/// Permission to clear one's own inventory.
CLEAR_SELF("clear.self"),
/// Permission to clear someone else's inventory.
CLEAR_OTHER("clear.other"),
/// Permission to view inventories and ender chests from other worlds.
ACCESS_CROSSWORLD("access.crossworld"),
/// Permission to access offline players' inventories and ender chests.
ACCESS_OFFLINE("access.offline"),
/// Permission to access online players' inventories and ender chests.
ACCESS_ONLINE("access.online"),
/// Permission granting the ability to edit players with the same access level.
ACCESS_EQUAL_EDIT("access.equal.edit"),
/// Permission to view, but not edit, players with the same access level.
ACCESS_EQUAL_VIEW("access.equal.view"),
/// Permission to deny access to players with the same access level.
ACCESS_EQUAL_DENY("access.equal.deny"),
/// Permission to perform inventory interaction while in spectator mode.
SPECTATE_CLICK("spectate.click"),
/// Permission to use the AnyContainer feature.
CONTAINER_ANY("container.any"),
/// Permission to use the AnyContainer feature but not the command to toggle it.
CONTAINER_ANY_USE("container.any.use"),
/// Permission to use the SilentContainer feature.
CONTAINER_SILENT("container.silent"),
/// Permission to use the SilentContainer feature but not the command to toggle it.
CONTAINER_SILENT_USE("container.silent.use"),
/// Permission to search inventories and ender chests.
SEARCH_INVENTORY("search.inventory"),
/// Permission to search containers.
SEARCH_CONTAINER("search.container");
private final String permission;
Permissions(String permission) {
this.permission = "openinv." + permission;
}
/**
* Check if a {@link Permissible} has this permission.
*
* @param permissible the Permissible
* @return true if the permission is granted
*/
public boolean hasPermission(Permissible permissible) {
return permissible.hasPermission(permission);
}
}
@@ -0,0 +1,47 @@
package com.lishid.openinv.util.setting;
import org.bukkit.entity.Player;
import org.jspecify.annotations.NullMarked;
import java.util.UUID;
/**
* A per-player setting that may be enabled or disabled.
*/
@NullMarked
public interface PlayerToggle {
/**
* Get the name of the setting.
*
* @return the setting name
*/
String getName();
/**
* Get the state of the toggle for a particular player ID.
*
* @param uuid the player ID
* @return true if the setting is enabled
*/
boolean is(UUID uuid);
/**
* Get the state of the toggle for a particular {@link Player},
* accounting for permissions required to use the feature.
*
* @param player the player
* @return true if the setting is enabled and the player has the required permissions
*/
boolean is(Player player);
/**
* Set the state of the toggle for a particular player ID.
*
* @param uuid the player ID
* @param enabled whether the setting is enabled
* @return true if the setting changed as a result of being set
*/
boolean set(UUID uuid, boolean enabled);
}
@@ -0,0 +1,121 @@
package com.lishid.openinv.util.setting;
import com.lishid.openinv.util.Permissions;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.UnmodifiableView;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* Utility class containing all of OpenInv's {@link PlayerToggle PlayerToggles}.
*/
@NullMarked
public final class PlayerToggles {
private static final Map<String, PlayerToggle> TOGGLES = new HashMap<>();
private static final PlayerToggle ANY = add(new MemoryToggle("AnyContainer") {
@Override
public boolean is(Player player) {
return is(player.getUniqueId())
&& (Permissions.CONTAINER_ANY.hasPermission(player) || Permissions.CONTAINER_ANY_USE.hasPermission(player));
}
});
private static final PlayerToggle SILENT = add(new MemoryToggle("SilentContainer") {
@Override
public boolean is(Player player) {
return is(player.getUniqueId())
&& (Permissions.CONTAINER_SILENT.hasPermission(player) || Permissions.CONTAINER_SILENT_USE.hasPermission(player));
}
});
/**
* Get the AnyContainer toggle.
*
* @return the AnyContainer toggle
*/
public static PlayerToggle any() {
return ANY;
}
/**
* Get the SilentContainer toggle.
*
* @return the SilentContainer toggle
*/
public static PlayerToggle silent() {
return SILENT;
}
/**
* Get a toggle by name.
*
* @param toggleName the name of the toggle
* @return the toggle, or null if no such toggle exists.
*/
public static @Nullable PlayerToggle get(String toggleName) {
PlayerToggle toggle = TOGGLES.get(toggleName);
if (toggle == null) {
toggle = TOGGLES.get(toggleName.toLowerCase(Locale.ENGLISH));
}
return toggle;
}
/**
* Get an unmodifable view of all toggles available.
*
* @return a view of all toggles available
*/
public static @UnmodifiableView Collection<PlayerToggle> get() {
return Collections.unmodifiableCollection(TOGGLES.values());
}
private static PlayerToggle add(PlayerToggle toggle) {
TOGGLES.put(toggle.getName().toLowerCase(Locale.ENGLISH), toggle);
return toggle;
}
private PlayerToggles() {
throw new IllegalStateException("Cannot create instance of utility class.");
}
private static abstract class MemoryToggle implements PlayerToggle {
private final Set<UUID> enabled;
private final String name;
private MemoryToggle(String name) {
enabled = new HashSet<>();
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public boolean is(UUID uuid) {
return enabled.contains(uuid);
}
@Override
public boolean set(UUID uuid, boolean enabled) {
if (enabled) {
return this.enabled.add(uuid);
} else {
return this.enabled.remove(uuid);
}
}
}
}
+47
View File
@@ -0,0 +1,47 @@
plugins {
`java-library`
alias(libs.plugins.paperweight) apply false
alias(libs.plugins.shadow) apply false
alias(libs.plugins.errorprone.gradle)
}
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
}
// Allow submodules to target higher Java release versions.
java.disableAutoTargetJvm()
subprojects {
apply(plugin = "java-library")
apply(plugin = rootProject.libs.plugins.errorprone.gradle.get().pluginId)
repositories {
mavenCentral()
}
dependencies {
compileOnly(rootProject.libs.jspecify)
compileOnly(rootProject.libs.annotations)
errorprone(rootProject.libs.errorprone.core)
}
tasks {
withType<JavaCompile>().configureEach {
options.encoding = Charsets.UTF_8.name()
}
withType<Javadoc>().configureEach {
options.encoding = Charsets.UTF_8.name()
}
}
}
// 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"))
}
+8
View File
@@ -0,0 +1,8 @@
plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
mavenCentral()
}
View File
Whitespace-only changes.
@@ -0,0 +1,107 @@
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 installDir: DirectoryProperty
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 spigotLocation = parameters.installDir.get().asFile.resolve("spigot-$version.jar")
// If Spigot is already installed, don't reinstall.
if (!parameters.ignoreCached.get() && spigotLocation.exists()) {
println("Skipping Spigot installation, $version is present")
return spigotLocation
}
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)
}.rethrowFailure()
val spigotBuild = buildTools.parentFile.resolve("Spigot/Spigot-Server/target/spigot-$version.jar")
if (!spigotBuild.exists()) {
throw IllegalStateException(
"Spigot build completed, but $spigotBuild does not exist! Did BuildTools change output location?"
)
}
spigotBuild.copyTo(spigotLocation, overwrite = true)
// Mark work for delete.
cleanUp(buildTools.parentFile)
if (!spigotLocation.exists()) {
throw IllegalStateException(
"Failed to install Spigot $version from $revision. Does the revision point to a different version?"
)
}
return spigotLocation
}
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,19 @@
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 classifier = objects.property(String::class.java).convention("")
val java = objects.property(JavaToolchainSpec::class.java)
val ignoreCached = objects.property(Boolean::class.java).convention(false)
}
@@ -0,0 +1,50 @@
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 java.io.File
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
)
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.
val spigot: File = target.providers.of(BuildToolsValueSource::class.java) {
parameters {
installDir.set(target.gradle.gradleUserHomeDir.resolve("caches/spigot"))
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(files(spigot))
target.dependencies.add("compileOnly", dependency)
}
}
}
+20
View File
@@ -0,0 +1,20 @@
repositories {
maven("https://hub.spigotmc.org/nexus/content/groups/public/")
}
dependencies {
compileOnly(libs.spigotapi)
}
java {
toolchain.languageVersion = JavaLanguageVersion.of(21)
}
tasks.withType<JavaCompile>().configureEach {
options.release = 21
}
dependencies {
implementation(project(":openinvapi"))
compileOnly(libs.slf4j.api)
}
@@ -0,0 +1,28 @@
package com.github.jikoo.openinv.internal;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public interface Accessor {
PlayerManager getPlayerManager();
IAnySilentContainer getAnySilentContainer();
ISpecialPlayerInventory createPlayerInventory(Player player);
ISpecialEnderChest createEnderChest(Player player);
<T extends ISpecialInventory> @Nullable T get(Inventory bukkitInventory, Class<T> clazz);
void reload(ConfigurationSection config);
}
@@ -0,0 +1,56 @@
/*
* 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.github.jikoo.openinv.internal;
import com.lishid.openinv.internal.ISpecialInventory;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public interface PlayerManager {
/**
* Loads a Player for an OfflinePlayer.
* </p>
* This method is potentially blocking, and should not be called on the main thread.
*
* @param offline the OfflinePlayer
* @return the Player loaded
*/
@Nullable Player loadPlayer(OfflinePlayer offline);
/**
* Creates a new Player from an existing one that will function slightly better offline.
*
* @return the Player
*/
Player inject(Player player);
/**
* Opens an ISpecialInventory for a Player.
*
* @param player the Player opening the ISpecialInventory
* @param inventory the Inventory
*
* @return the InventoryView opened
*/
@Nullable InventoryView openInventory(Player player, ISpecialInventory inventory, boolean viewOnly);
}
@@ -0,0 +1,107 @@
package com.github.jikoo.openinv.internal.container;
import com.lishid.openinv.internal.IAnySilentContainer;
import org.bukkit.block.Barrel;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.EnderChest;
import org.bukkit.block.ShulkerBox;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Chest;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public abstract class AnySilentContainerBase implements IAnySilentContainer {
public enum StateType {
ENDER_CHEST,
SHULKER_BOX,
CHEST,
BARREL,
OTHER
}
@Override
public boolean isAnyContainerNeeded(Block block) {
return isAnyContainerNeeded(getStateType(block), block);
}
protected boolean isAnyContainerNeeded(StateType material, Block block) {
return switch (material) {
// Enderchests require a non-occluding block on top to open.
case ENDER_CHEST -> block.getRelative(0, 1, 0).getType().isOccluding();
// Shulker boxes require half a block clear in the direction they open.
case SHULKER_BOX -> isShulkerBlocked(block);
// Chests require that the block above not be occluding or occupied by a cat.
case CHEST -> isChestBlocked(block);
// Barrels need silent but not any. All others unsupported.
default -> false;
};
}
@Override
public boolean isChestBlocked(Block block) {
if (IAnySilentContainer.super.isChestBlocked(block)) {
return true;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Chest chest) || chest.getType() == Chest.Type.SINGLE) {
return false;
}
BlockFace relativeFace = switch (chest.getFacing()) {
case NORTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.WEST : BlockFace.EAST;
case EAST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.NORTH : BlockFace.SOUTH;
case SOUTH -> chest.getType() == Chest.Type.RIGHT ? BlockFace.EAST : BlockFace.WEST;
case WEST -> chest.getType() == Chest.Type.RIGHT ? BlockFace.SOUTH : BlockFace.NORTH;
default -> BlockFace.SELF;
};
Block relative = block.getRelative(relativeFace);
if (relative.getType() != block.getType()) {
return false;
}
BlockData relativeData = relative.getBlockData();
if (!(relativeData instanceof Chest relativeChest)) {
return false;
}
if (relativeChest.getFacing() != chest.getFacing()
|| relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) {
return false;
}
return IAnySilentContainer.super.isChestBlocked(relative);
}
@Override
public boolean isAnySilentContainer(Block block) {
return isAnySilentContainer(getState(block));
}
@Override
public boolean isAnySilentContainer(Inventory inventory) {
return isAnySilentContainer(getHolder(inventory));
}
protected StateType getStateType(Block block) {
return switch (getState(block)) {
case Barrel ignored -> StateType.BARREL;
case EnderChest ignored -> StateType.ENDER_CHEST;
case ShulkerBox ignored -> StateType.SHULKER_BOX;
case org.bukkit.block.Chest ignored -> StateType.CHEST;
default -> StateType.OTHER;
};
}
protected abstract BlockState getState(Block block);
protected abstract @Nullable InventoryHolder getHolder(Inventory inventory);
}
@@ -0,0 +1,10 @@
package com.github.jikoo.openinv.internal.container;
import org.jspecify.annotations.NullMarked;
@NullMarked
public interface InternalOwned<T> {
T getOwnerHandle();
}
@@ -0,0 +1,4 @@
package com.github.jikoo.openinv.internal.container;
public interface ViewOnly {
}
@@ -0,0 +1,66 @@
package com.github.jikoo.openinv.internal.container.slot;
import org.jspecify.annotations.NullMarked;
/**
* An interface defining behaviors for entries in a {@link net.minecraft.world.Container Container}. Used to reduce duplicate content reordering.
*/
@NullMarked
public interface Content<P, I, C, S> {
/**
* Update internal holder.
*
* @param holder the new holder
*/
void setHolder(P holder);
/**
* Get the current item.
*
* @return the current item
*/
I get();
/**
* Remove the current item.
*
* @return the current item
*/
I remove();
/**
* Remove some of the current item.
*
* @return the current item
*/
I removePartial(int amount);
/**
* Set the current item. If slot is currently not usable, will drop item instead.
*
* @param itemStack the item to set
*/
void set(I itemStack);
/**
* Get a {@link net.minecraft.world.inventory.Slot Slot} for use in a {@link net.minecraft.world.inventory.AbstractContainerMenu ContainerMenu}. Will
* impose any specific restrictions to insertion or removal.
*
* @param container the backing container
* @param slot the slot of the backing container represented
* @param x clientside x dimension from top left of inventory, not used
* @param y clientside y dimension from top left of inventory, not used
* @return a menu slot
*/
S asSlot(C container, int slot, int x, int y);
/**
* Get a loose Bukkit translation of what this slot stores. For example, any slot that drops items at the owner rather
* than insert them will report itself as being {@link org.bukkit.event.inventory.InventoryType.SlotType#OUTSIDE}.
*
* @return the closes Bukkit slot type
*/
org.bukkit.event.inventory.InventoryType.SlotType getSlotType();
}
@@ -0,0 +1,35 @@
package com.github.jikoo.openinv.internal.container.slot;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
import org.jspecify.annotations.NullMarked;
import java.util.List;
import java.util.function.Function;
@NullMarked
public interface InventoryFactory<P, I, C, S, E> {
Content<P, I, C, S> newCrafting(P holder, int index);
Content<P, I, C, S> newCraftingResult(P holder);
Content<P, I, C, S> newCursor(P holder);
Content<P, I, C, S> newDrop(P holder);
Content<P, I, C, S> newEquipment(P holder, E equipmentSlot);
Content<P, I, C, S> newList(P holder, int index, InventoryType.SlotType slotType, Function<P, List<I>> listSupplier);
Content<P, I, C, S> newOffHand(P holder);
Content<P, I, C, S> newViewOnly(P holder);
Content<P, I, C, S> newViewOnly(P holder, I itemStack);
S wrapViewOnly(S wrapped);
ItemStack asBukkitMirror(I itemStack);
}
@@ -0,0 +1,309 @@
package com.github.jikoo.openinv.lang;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Predicate;
import java.util.logging.Level;
/**
* A simple language manager supporting both custom and bundled languages.
*/
@NullMarked
public class LanguageManager {
private final Plugin plugin;
private final File folder;
private final String defaultLocale;
private final Map<String, YamlConfiguration> locales;
public LanguageManager(Plugin plugin, String defaultLocale) {
this.plugin = plugin;
this.defaultLocale = defaultLocale;
this.locales = new HashMap<>();
this.folder = new File(plugin.getDataFolder(), "locale");
if (!folder.exists() && !folder.mkdirs()) {
plugin.getLogger().warning(() -> "Unable to create " + folder.getPath() + "! Languages may not be editable.");
}
reload();
}
public void reload() {
this.locales.clear();
getOrLoadLocale(defaultLocale);
}
private YamlConfiguration getOrLoadLocale(String locale) {
YamlConfiguration loaded = locales.get(locale);
if (loaded != null) {
return loaded;
}
LangLocation lang = bestMatch(locale, null);
// If a parent was a better match, check if it is already loaded.
if (!locale.equals(lang.locale)) {
loaded = locales.get(lang.locale);
if (loaded != null) {
locales.put(locale, loaded);
return loaded;
}
}
// Load locale config from disk and bundled locale defaults.
YamlConfiguration localeConfig = loadLocale(lang);
// If the locale is not the default locale, also handle any missing translations from the default locale.
if (!locale.equals(defaultLocale)) {
addTranslationFallthrough(lang, localeConfig);
if (plugin.getConfig().getBoolean("settings.secret.warn-about-guess-section", true)
&& localeConfig.isConfigurationSection("guess")) {
// Warn that guess section exists. This should run once per language per server restart
// when accessed by a user to hint to server owners that they can make UX improvements.
plugin.getLogger().info(() -> "[LanguageManager] Missing translations from " + lang.locale + ".yml! Check the guess section!");
}
}
locales.put(locale, localeConfig);
locales.put(lang.locale, localeConfig);
return localeConfig;
}
private LangLocation bestMatch(String locale, @Nullable LangLocation initial) {
File file = new File(folder, locale + ".yml");
InputStream bundled = plugin.getResource("locale/" + locale + ".yml");
if (file.exists() || bundled != null) {
return new LangLocation(locale, file, bundled);
}
if (initial == null) {
initial = new LangLocation(locale, file, null);
}
int lastSeparator = locale.lastIndexOf('_');
// Must be at least some content before separator.
if (lastSeparator < 1) {
return initial;
}
return bestMatch(locale.substring(0, lastSeparator), initial);
}
private YamlConfiguration loadLocale(LangLocation lang) {
YamlConfiguration localeConfigDefaults;
if (lang.bundled == null) {
localeConfigDefaults = new YamlConfiguration();
} else {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(lang.bundled, StandardCharsets.UTF_8))) {
localeConfigDefaults = YamlConfiguration.loadConfiguration(reader);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to load resource " + lang.locale + ".yml");
localeConfigDefaults = new YamlConfiguration();
}
}
if (!lang.file.exists()) {
// If the file does not exist on disk, save bundled defaults.
try {
localeConfigDefaults.save(lang.file);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml");
}
// Return loaded bundled locale.
return localeConfigDefaults;
}
// If the file does exist on disk, load it.
YamlConfiguration localeConfig = YamlConfiguration.loadConfiguration(lang.file);
// Check for missing translations from the bundled file.
List<String> newKeys = getMissingKeys(localeConfigDefaults, localeConfig::isSet);
if (newKeys.isEmpty()) {
return localeConfig;
}
// Get guess section for missing keys.
ConfigurationSection guess = localeConfig.getConfigurationSection("guess");
for (String newKey : newKeys) {
// Set all missing keys to defaults.
localeConfig.set(newKey, localeConfigDefaults.get(newKey));
// Delete relevant guess keys in case this is a new translation.
if (guess != null) {
guess.set(newKey, null);
}
}
// If guess section is empty, delete it.
if (guess != null && guess.getKeys(false).isEmpty()) {
localeConfig.set("guess", null);
}
plugin.getLogger().info(() -> "[LanguageManager] Added new translation keys to " + lang.locale + ".yml: " + String.join(", ", newKeys));
// Write new keys to disk.
try {
localeConfig.save(lang.file);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + lang.locale + ".yml");
}
return localeConfig;
}
private void addTranslationFallthrough(LangLocation location, YamlConfiguration localeConfig) {
YamlConfiguration defaultLocaleConfig = locales.get(defaultLocale);
// Get missing keys. Keys that already have a guess value are not new and don't need to trigger another write.
List<String> missingKeys = getMissingKeys(
defaultLocaleConfig,
key -> localeConfig.isSet(key) || localeConfig.isSet("guess." + key)
);
if (!missingKeys.isEmpty()) {
// Set up guess section for missing keys.
for (String key : missingKeys) {
localeConfig.set("guess." + key, defaultLocaleConfig.get(key));
}
// Write modified guess section to disk.
try {
localeConfig.save(location.file);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, e, () -> "[LanguageManager] Unable to save resource " + location.locale + ".yml");
}
}
// Fall through to default locale.
localeConfig.setDefaults(defaultLocaleConfig);
}
private List<String> getMissingKeys(
Configuration configurationDefault,
Predicate<String> nodeSetPredicate
) {
List<String> missingKeys = new ArrayList<>();
for (String key : configurationDefault.getKeys(true)) {
if (!configurationDefault.isConfigurationSection(key) && !nodeSetPredicate.test(key)) {
// Missing keys are non-section keys that fail the predicate.
missingKeys.add(key);
}
}
return missingKeys;
}
public @Nullable String getValue(String key, @Nullable String locale) {
String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase(Locale.ENGLISH)).getString(key);
if (value == null || value.isBlank()) {
return null;
}
value = ChatColor.translateAlternateColorCodes('&', value);
return value;
}
public @Nullable String getValue(
String key,
@Nullable String locale,
Replacement... replacements
) {
String value = getValue(key, locale);
if (value == null) {
return null;
}
for (Replacement replacement : replacements) {
value = value.replace(replacement.placeholder(), replacement.value());
}
return value;
}
public @Nullable String getLocalizedMessage(CommandSender sender, String key) {
return getValue(key, getLocale(sender));
}
public @Nullable String getLocalizedMessage(
CommandSender sender,
String key,
Replacement... replacements
) {
return getValue(key, getLocale(sender), replacements);
}
private String getLocale(CommandSender sender) {
if (sender instanceof Player player) {
return player.getLocale();
} else {
return plugin.getConfig().getString("settings.locale", "en");
}
}
public void sendMessage(CommandSender sender, String key) {
String message = getLocalizedMessage(sender, key);
if (message != null && !message.isEmpty()) {
sender.sendMessage(message);
}
}
public void sendMessage(CommandSender sender, String key, Replacement... replacements) {
String message = getLocalizedMessage(sender, key, replacements);
if (message != null && !message.isEmpty()) {
sender.sendMessage(message);
}
}
public void sendSystemMessage(Player player, String key) {
String message = getLocalizedMessage(player, key);
if (message == null) {
return;
}
int newline = message.indexOf('\n');
if (newline != -1) {
// No newlines in action bar chat.
message = message.substring(0, newline);
}
if (message.isEmpty()) {
return;
}
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacy(message));
}
private record LangLocation(String locale, File file, @Nullable InputStream bundled) {}
}
@@ -0,0 +1,14 @@
package com.github.jikoo.openinv.lang;
import org.jspecify.annotations.NullMarked;
/**
* A data holder for string replacement in translations.
*
* @param placeholder the placeholder to be replaced
* @param value the value to insert
*/
@NullMarked
public record Replacement(String placeholder, String value) {
}
@@ -0,0 +1,131 @@
package com.github.jikoo.openinv.util;
import org.jspecify.annotations.NullMarked;
import org.slf4j.Marker;
import org.slf4j.helpers.LegacyAbstractLogger;
import org.slf4j.helpers.MessageFormatter;
import org.slf4j.helpers.NormalizedParameters;
import org.slf4j.spi.LocationAwareLogger;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* An adapter for wrapping a {@link java.util.logging.Logger} as a {@link org.slf4j.Logger}.
* <br>Largely based on {@code JDK14LoggerAdapter}, which is not present at runtime.
*/
@NullMarked
public class JulLoggerAdapter extends LegacyAbstractLogger implements LocationAwareLogger {
private final Logger wrapped;
public JulLoggerAdapter(Logger wrapped) {
this.wrapped = wrapped;
this.name = wrapped.getName();
}
@Override
protected String getFullyQualifiedCallerName() {
return JulLoggerAdapter.class.getName();
}
@Override
protected void handleNormalizedLoggingCall(
org.slf4j.event.Level slf4jLevel,
Marker marker,
String msg,
Object[] args,
Throwable thrown
) {
Level level = fromSlf4jLevel(slf4jLevel);
if (wrapped.isLoggable(level)) {
normalizedLog(getFullyQualifiedCallerName(), level, msg, args, thrown);
}
}
private void normalizedLog(String fqcn, Level level, String msg, Object[] args, Throwable thrown) {
String formatted = MessageFormatter.basicArrayFormat(msg, args);
LogRecord logRecord = new LogRecord(level, formatted);
logRecord.setLoggerName(getName());
logRecord.setThrown(thrown);
addSource(fqcn, logRecord);
wrapped.log(logRecord);
}
private void addSource(String fqcn, LogRecord logRecord) {
StackWalker.getInstance().walk(stream -> stream
// Start at index 2 - this is an internal method, so we want at least the caller's caller.
.skip(2)
// Walk at maximum 10 frames.
.limit(10)
.filter(frame -> isIgnored(frame.getClassName(), fqcn))
.findFirst()
).ifPresent(frame -> {
logRecord.setSourceClassName(frame.getClassName());
logRecord.setSourceMethodName(frame.getMethodName());
});
}
private boolean isIgnored(String className, String fqcn) {
if (className.equals(fqcn)) {
return true;
}
// Ignore slf4j classes - they shouldn't be the source.
if (className.startsWith("org.slf4j.")) {
return true;
}
return className.equals(getFullyQualifiedCallerName());
}
@Override
public void log(Marker marker, String callerFqn, int levelInt, String msg, Object[] args, Throwable thrown) {
Level level = fromSlf4jLevel(org.slf4j.event.Level.intToLevel(levelInt));
if (!wrapped.isLoggable(level)) {
return;
}
NormalizedParameters params = NormalizedParameters.normalize(msg, args, thrown);
normalizedLog(callerFqn, level, params.getMessage(), params.getArguments(), params.getThrowable());
}
private Level fromSlf4jLevel(org.slf4j.event.Level level) {
return switch (level) {
case TRACE -> Level.FINEST;
case DEBUG -> Level.FINE;
case INFO -> Level.INFO;
case WARN -> Level.WARNING;
case ERROR -> Level.SEVERE;
};
}
@Override
public boolean isTraceEnabled() {
return wrapped.isLoggable(Level.FINEST);
}
@Override
public boolean isDebugEnabled() {
return wrapped.isLoggable(Level.FINE);
}
@Override
public boolean isInfoEnabled() {
return wrapped.isLoggable(Level.INFO);
}
@Override
public boolean isWarnEnabled() {
return wrapped.isLoggable(Level.WARNING);
}
@Override
public boolean isErrorEnabled() {
return wrapped.isLoggable(Level.SEVERE);
}
}
@@ -0,0 +1,64 @@
package com.github.jikoo.openinv.util;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.lang.reflect.Field;
/**
* A utility for making reflection easier.
*/
@NullMarked
public final class ReflectionHelper {
/**
* Grab an {@link Object} stored in a {@link Field} of another {@code Object}.
*
* <p>This casts the field to the correct class. Any issues will result in a {@code null} return value.
*
* @param fieldType the {@link Class} of {@code Object} stored in the {@code Field}
* @param holder the containing {@code Object}
* @param <T> the type of stored {@code Object}
* @return the first matching {@code Object} or {@code null} if none match
*/
public static <T> @Nullable T grabObjectByType(final Object holder, final Class<T> fieldType) {
Field field = grabFieldByType(holder.getClass(), fieldType);
if (field != null) {
try {
return fieldType.cast(field.get(holder));
} catch (IllegalAccessException ignored) {
// Ignore issues obtaining field
}
}
return null;
}
/**
* Grab a {@link Field} of an {@link Object}
*
* @param fieldType the {@link Class} of the object
* @param holderType the containing {@code Class}
* @return the first matching object or {@code null} if none match
*/
public static @Nullable Field grabFieldByType(Class<?> holderType, Class<?> fieldType) {
for (Field field : holderType.getDeclaredFields()) {
if (fieldType.isAssignableFrom(field.getType())) {
field.setAccessible(true);
return field;
}
}
if (holderType.getSuperclass() != null) {
return grabFieldByType(fieldType, holderType.getSuperclass());
}
return null;
}
private ReflectionHelper() {
throw new IllegalStateException("Cannot create instance of utility class.");
}
}
@@ -0,0 +1,40 @@
package com.lishid.openinv.event;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.util.setting.PlayerToggle;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.jspecify.annotations.NullMarked;
import java.util.UUID;
/**
* Construct and call events.
*/
@NullMarked
public final class OpenEvents {
public static boolean saveCancelled(Player player) {
return call(new PlayerSaveEvent(player));
}
public static boolean saveCancelled(ISpecialInventory inventory) {
return call(new OpenPlayerSaveEvent((Player) inventory.getPlayer(), inventory));
}
public static void notifyPlayerToggle(PlayerToggle toggle, UUID uuid, boolean state) {
Bukkit.getPluginManager().callEvent(new PlayerToggledEvent(toggle, uuid, state));
}
private static <T extends Event & Cancellable> boolean call(T event) {
Bukkit.getPluginManager().callEvent(event);
return event.isCancelled();
}
private OpenEvents() {
throw new IllegalStateException("Cannot create instance of utility class.");
}
}
+9
View File
@@ -0,0 +1,9 @@
# Project meta
group=com.lishid.openinv
version=5.3.4
description=A Bukkit plugin for opening normally-inaccessible inventories.
# Gradle configuration
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
+33
View File
@@ -0,0 +1,33 @@
[versions]
spigotapi = "1.21.3-R0.1-SNAPSHOT"
planarwrappers = "3.3.0"
jspecify = "1.0.1"
annotations = "26.1.0"
paperweight = "2.0.0-beta.21"
shadow = "9.6.1"
folia-scheduler-wrapper = "v0.0.3"
errorprone-core = "2.50.0"
errorprone-gradle = "5.1.1"
slf4j = "2.0.18"
sqlite-jdbc = "3.49.1.0"
mockito-core = "5.23.0"
junit-jupiter = "6.1.3"
hamcrest = "3.0"
[libraries]
spigotapi = { module = "org.spigotmc:spigot-api", version.ref = "spigotapi" }
planarwrappers = { module = "com.github.jikoo:planarwrappers", version.ref = "planarwrappers" }
jspecify = { module = "org.jspecify:jspecify", version.ref = "jspecify" }
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" }
errorprone-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorprone-core" }
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
sqlite-jdbc = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite-jdbc" }
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito-core" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
[plugins]
paperweight = { id = "io.papermc.paperweight.userdev", version.ref = "paperweight" }
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
errorprone-gradle = { id = "net.ltgt.errorprone", version.ref = "errorprone-gradle" }
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+248
View File
@@ -0,0 +1,248 @@
#!/bin/sh
#
# Copyright © 2015 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.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
# gradlew 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 gradlew
#
# 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/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/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
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# 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
# 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
if ! command -v java >/dev/null 2>&1
then
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
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
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" )
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
# 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"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# 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
+82
View File
@@ -0,0 +1,82 @@
@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
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem gradlew startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables, and ensure extensions are enabled
setlocal EnableExtensions
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
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% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
"%COMSPEC%" /c exit 1
:execute
@rem Setup the command line
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:exitWithErrorLevel
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
"%COMSPEC%" /c exit %ERRORLEVEL%
+25
View File
@@ -0,0 +1,25 @@
plugins {
alias(libs.plugins.paperweight)
}
java {
toolchain.languageVersion = JavaLanguageVersion.of(21)
}
tasks.withType<JavaCompile>().configureEach {
options.release = 21
}
dependencies {
implementation(project(":openinvapi")) {
exclude(group = "org.spigotmc", module = "spigot-api")
}
implementation(project(":openinvcommon")) {
exclude(group = "org.spigotmc", module = "spigot-api")
}
implementation(project(":openinvadapterpaper26_1")) {
exclude(group = "io.papermc.paper", module = "dev-bundle")
}
paperweight.paperDevBundle("1.21.11-R0.1-SNAPSHOT")
}
@@ -0,0 +1,30 @@
package com.lishid.openinv.internal.paper1_21_11;
import com.github.jikoo.openinv.lang.LanguageManager;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.internal.paper1_21_11.container.OpenEnderChest;
import com.lishid.openinv.internal.paper1_21_11.container.OpenInventory;
import org.bukkit.entity.Player;
import org.jspecify.annotations.NullMarked;
import java.util.logging.Logger;
@NullMarked
public class InternalAccessor extends com.lishid.openinv.internal.paper26_1.InternalAccessor {
public InternalAccessor(Logger logger, LanguageManager lang) {
super(logger, lang);
}
@Override
public ISpecialEnderChest createEnderChest(Player player) {
return new OpenEnderChest(player);
}
@Override
public ISpecialPlayerInventory createPlayerInventory(Player player) {
return new OpenInventory(factory, player);
}
}
@@ -0,0 +1,35 @@
package com.lishid.openinv.internal.paper1_21_11.container;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper1_21_11.container.menu.OpenEnderChestMenu;
import com.lishid.openinv.internal.paper26_3.container.menu.OpenChestMenu;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class OpenEnderChest extends com.lishid.openinv.internal.paper26_3.container.OpenEnderChest {
public OpenEnderChest(org.bukkit.entity.Player player) {
super(player);
}
@Override
public @Nullable OpenChestMenu<?> createMenu(
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
Player player,
int i,
boolean viewOnly
) {
if (player instanceof ServerPlayer serverPlayer) {
return new OpenEnderChestMenu(factory, this, serverPlayer, i, viewOnly);
}
return null;
}
}
@@ -0,0 +1,33 @@
package com.lishid.openinv.internal.paper1_21_11.container;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper1_21_11.container.menu.OpenInventoryMenu;
import com.lishid.openinv.internal.paper26_3.container.menu.OpenChestMenu;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class OpenInventory extends com.lishid.openinv.internal.paper26_3.container.OpenInventory {
public OpenInventory(
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
org.bukkit.entity.Player bukkitPlayer
) {
super(factory, bukkitPlayer);
}
@Override
public @Nullable OpenChestMenu<?> createMenu(Player player, int i, boolean viewOnly) {
if (player instanceof ServerPlayer serverPlayer) {
return new OpenInventoryMenu(factory, this, serverPlayer, i, viewOnly);
}
return null;
}
}
@@ -0,0 +1,72 @@
package com.lishid.openinv.internal.paper1_21_11.container.menu;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.OpenEnderChest;
import com.lishid.openinv.internal.paper26_3.container.menu.OpenSyncMenu;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class OpenEnderChestMenu extends OpenSyncMenu<OpenEnderChest> {
public OpenEnderChestMenu(
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
OpenEnderChest enderChest,
ServerPlayer viewer,
int containerId,
boolean viewOnly
) {
super(getChestMenuType(enderChest.getContainerSize()), containerId, factory, enderChest, viewer, viewOnly);
}
@Override
public ItemStack quickMoveStack(Player player, int index) {
if (viewOnly) {
return ItemStack.EMPTY;
}
// See ChestMenu
Slot slot = this.slots.get(index);
if (slot.isFake() || !slot.hasItem()) {
return ItemStack.EMPTY;
}
ItemStack itemStack = slot.getItem();
ItemStack original = itemStack.copy();
if (index < topSize) {
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
return ItemStack.EMPTY;
}
} else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) {
return ItemStack.EMPTY;
}
if (itemStack.isEmpty()) {
slot.setByPlayer(ItemStack.EMPTY);
} else {
slot.setChanged();
}
return original;
}
@Override
public void clicked(int i, int j, ClickType clickType, Player player) {
if (viewOnly) {
if (clickType == ClickType.QUICK_CRAFT) {
sendAllDataToRemote();
}
return;
}
super.clicked(i, j, clickType, player);
}
}
@@ -0,0 +1,39 @@
package com.lishid.openinv.internal.paper1_21_11.container.menu;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.BaseOpenInventory;
import com.lishid.openinv.internal.paper26_3.container.menu.BaseOpenInventoryMenu;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class OpenInventoryMenu extends BaseOpenInventoryMenu {
public OpenInventoryMenu(
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
BaseOpenInventory inventory,
ServerPlayer viewer,
int i,
boolean viewOnly
) {
super(factory, inventory, viewer, i, viewOnly);
}
@Override
public void clicked(int i, int j, ClickType clickType, Player player) {
if (viewOnly) {
if (clickType == ClickType.QUICK_CRAFT) {
sendAllDataToRemote();
}
return;
}
super.clicked(i, j, clickType, player);
}
}
+45
View File
@@ -0,0 +1,45 @@
plugins {
alias(libs.plugins.paperweight)
}
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
}
// Paper is compiled with Java 25, so we have to use a Java 25 toolchain.
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
}
// To minimize duplicate code, the adapter for 1.21.11 is built off of this module, so we need to target Java 21.
tasks.withType<JavaCompile>().configureEach {
options.release = 21
}
// As a result, we have to specifically tell Gradle it's okay if our dependencies require Java 25,
// because secretly sometimes they don't!
configurations.matching { it.isCanBeResolved && it.name.endsWith("Classpath") }.all {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 25)
}
}
dependencies {
implementation(project(":openinvapi")) {
exclude(group = "org.spigotmc", module = "spigot-api")
}
implementation(project(":openinvcommon")) {
exclude(group = "org.spigotmc", module = "spigot-api")
}
api(project(":openinvadapterpaper26_2")) {
exclude(group = "io.papermc.paper", module = "dev-bundle")
}
paperweight.paperDevBundle("26.1.2.build.+")
}
tasks.reobfJar {
enabled = false
}
configurations.reobf.get().outgoing.artifacts.clear()
@@ -0,0 +1,53 @@
package com.lishid.openinv.internal.paper26_1;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_1.container.AnySilentContainer;
import com.lishid.openinv.internal.paper26_1.container.OpenEnderChest;
import com.lishid.openinv.internal.paper26_1.player.PlayerManager;
import com.lishid.openinv.internal.paper26_3.container.slot.placeholder.PlaceholderLoader;
import com.github.jikoo.openinv.lang.LanguageManager;
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.entity.Player;
import org.jspecify.annotations.NullMarked;
import java.util.logging.Logger;
@NullMarked
public class InternalAccessor extends com.lishid.openinv.internal.paper26_2.InternalAccessor {
public InternalAccessor(Logger logger, LanguageManager lang) {
super(logger, lang);
}
@Override
protected PlayerManager createPlayerManager(
Logger logger,
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory
) {
return new PlayerManager(logger, factory);
}
@Override
protected AnySilentContainer createAnySilentContainer(
Logger logger,
LanguageManager lang
) {
return new AnySilentContainer(logger, lang);
}
@Override
protected PlaceholderLoader createPlaceholderLoader() {
return new com.lishid.openinv.internal.paper26_1.container.slot.placeholder.PlaceholderLoader();
}
@Override
public ISpecialEnderChest createEnderChest(Player player) {
return new OpenEnderChest(player);
}
}
@@ -0,0 +1,21 @@
package com.lishid.openinv.internal.paper26_1.container;
import com.github.jikoo.openinv.lang.LanguageManager;
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
import org.jspecify.annotations.NullMarked;
import java.util.logging.Logger;
@NullMarked
public class AnySilentContainer extends com.lishid.openinv.internal.paper26_3.container.AnySilentContainer {
public AnySilentContainer(Logger logger, LanguageManager lang) {
super(logger, lang);
}
@Override
protected boolean hasLootTable(RandomizableContainerBlockEntity lootable) {
return lootable.lootTable != null;
}
}
@@ -0,0 +1,20 @@
package com.lishid.openinv.internal.paper26_1.container;
import net.minecraft.core.NonNullList;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class OpenEnderChest extends com.lishid.openinv.internal.paper26_3.container.OpenEnderChest {
public OpenEnderChest(org.bukkit.entity.Player player) {
super(player);
}
@Override
protected NonNullList<ItemStack> getEnderChestItems(ServerPlayer owner) {
return owner.getEnderChestInventory().items;
}
}
@@ -0,0 +1,20 @@
package com.lishid.openinv.internal.paper26_1.container.slot.placeholder;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class PlaceholderLoader extends com.lishid.openinv.internal.paper26_3.container.slot.placeholder.PlaceholderLoader {
@Override
protected Item getDefaultCursorItem() {
return Items.WHITE_BANNER;
}
@Override
protected Item getDefaultNotSlotItem() {
return Items.WHITE_STAINED_GLASS_PANE;
}
}
@@ -0,0 +1,50 @@
package com.lishid.openinv.internal.paper26_1.player;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import net.minecraft.server.level.ServerLevel;
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 net.minecraft.world.level.storage.LevelData;
import net.minecraft.world.phys.Vec3;
import org.jspecify.annotations.NullMarked;
import java.util.logging.Logger;
@NullMarked
public class PlayerManager extends com.lishid.openinv.internal.paper26_3.player.PlayerManager {
public PlayerManager(
Logger logger,
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory
) {
super(logger, factory);
}
@Override
protected void removeAdvancementListeners(ServerPlayer entity) {
entity.getAdvancements().stopListening();
}
@Override
protected LevelData.RespawnData getRespawnData(ServerLevel level) {
return level.levelData.getRespawnData();
}
@Override
protected Vec3 getAdjustedSpawnLocation(
ServerPlayer player,
ServerLevel level,
LevelData.RespawnData respawnData
) {
return player.adjustSpawnLocation(level, respawnData.pos()).getBottomCenter();
}
@Override
protected void setServerLevel(ServerPlayer player, ServerLevel level) {
player.spawnIn(level);
}
}
+45
View File
@@ -0,0 +1,45 @@
plugins {
alias(libs.plugins.paperweight)
}
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
}
// Paper is compiled with Java 25, so we have to use a Java 25 toolchain.
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
}
// To minimize duplicate code, the adapter for 1.21.11 is built off of this module, so we need to target Java 21.
tasks.withType<JavaCompile>().configureEach {
options.release = 21
}
// As a result, we have to specifically tell Gradle it's okay if our dependencies require Java 25,
// because secretly sometimes they don't!
configurations.matching { it.isCanBeResolved && it.name.endsWith("Classpath") }.all {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 25)
}
}
dependencies {
implementation(project(":openinvapi")) {
exclude(group = "org.spigotmc", module = "spigot-api")
}
implementation(project(":openinvcommon")) {
exclude(group = "org.spigotmc", module = "spigot-api")
}
api(project(":openinvadapterpaper26_3")) {
exclude(group = "io.papermc.paper", module = "dev-bundle")
}
paperweight.paperDevBundle("26.2.build.+")
}
tasks.reobfJar {
enabled = false
}
configurations.reobf.get().outgoing.artifacts.clear()
@@ -0,0 +1,27 @@
package com.lishid.openinv.internal.paper26_2;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_2.container.slot.OpenInventoryFactory;
import com.github.jikoo.openinv.lang.LanguageManager;
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.jspecify.annotations.NullMarked;
import java.util.logging.Logger;
@NullMarked
public class InternalAccessor extends com.lishid.openinv.internal.paper26_3.InternalAccessor {
public InternalAccessor(Logger logger, LanguageManager lang) {
super(logger, lang);
}
@Override
protected InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> createInventoryFactory() {
return new OpenInventoryFactory();
}
}
@@ -0,0 +1,95 @@
package com.lishid.openinv.internal.paper26_2.container.slot;
import com.github.jikoo.openinv.internal.container.slot.Content;
import com.lishid.openinv.internal.paper26_3.container.slot.ContentCrafting;
import com.lishid.openinv.internal.paper26_3.container.slot.ContentCursor;
import com.lishid.openinv.internal.paper26_3.container.slot.ContentDrop;
import com.lishid.openinv.internal.paper26_3.container.slot.ContentViewOnly;
import com.lishid.openinv.internal.paper26_3.container.slot.SlotViewOnly;
import com.lishid.openinv.internal.paper26_3.container.slot.placeholder.Placeholders;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class OpenInventoryFactory extends com.lishid.openinv.internal.paper26_3.container.slot.OpenInventoryFactory {
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newCrafting(
ServerPlayer holder,
int index
) {
return new ContentCrafting(holder, index) {
@Override
public void set(ItemStack itemStack) {
if (isAvailable(holder)) {
items.set(index, itemStack);
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
} else {
this.holder.drop(itemStack, false);
}
}
};
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newCursor(ServerPlayer holder) {
return new ContentCursor(holder) {
@Override
public void set(ItemStack itemStack) {
if (isAvailable()) {
holder.containerMenu.setCarried(itemStack);
} else {
this.holder.drop(itemStack, false);
}
}
};
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newDrop(ServerPlayer holder) {
return new ContentDrop(holder) {
@Override
public void set(ItemStack itemStack) {
this.holder.drop(itemStack, false);
}
};
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newViewOnly(ServerPlayer holder) {
return newViewOnly(holder, Placeholders.notSlot);
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newViewOnly(
ServerPlayer holder,
ItemStack itemStack
) {
return new ContentViewOnly(holder) {
@Override
public void set(ItemStack itemStack) {
this.holder.drop(itemStack, false);
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotViewOnly(container, slot, x, y) {
@Override
public ItemStack getOrDefault() {
return itemStack;
}
};
}
};
}
@Override
public org.bukkit.inventory.ItemStack asBukkitMirror(ItemStack itemStack) {
return CraftItemStack.asCraftMirror(itemStack);
}
}
+42
View File
@@ -0,0 +1,42 @@
plugins {
alias(libs.plugins.paperweight)
}
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
}
// Paper is compiled with Java 25, so we have to use a Java 25 toolchain.
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(25))
}
// To minimize duplicate code, the adapter for 1.21.11 is built off of this module, so we need to target Java 21.
tasks.withType<JavaCompile>().configureEach {
options.release = 21
}
// As a result, we have to specifically tell Gradle it's okay if our dependencies require Java 25,
// because secretly sometimes they don't!
configurations.matching { it.isCanBeResolved && it.name.endsWith("Classpath") }.all {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 25)
}
}
dependencies {
implementation(project(":openinvapi")) {
exclude(group = "org.spigotmc", module = "spigot-api")
}
implementation(project(":openinvcommon")) {
exclude(group = "org.spigotmc", module = "spigot-api")
}
paperweight.paperDevBundle("26.3.build.+")
}
tasks.reobfJar {
enabled = false
}
configurations.reobf.get().outgoing.artifacts.clear()
@@ -0,0 +1,111 @@
package com.lishid.openinv.internal.paper26_3;
import com.github.jikoo.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.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.AnySilentContainer;
import com.lishid.openinv.internal.paper26_3.container.OpenEnderChest;
import com.lishid.openinv.internal.paper26_3.container.OpenInventory;
import com.lishid.openinv.internal.paper26_3.container.slot.OpenInventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.slot.placeholder.PlaceholderLoader;
import com.lishid.openinv.internal.paper26_3.player.PlayerManager;
import com.github.jikoo.openinv.lang.LanguageManager;
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.configuration.ConfigurationSection;
import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.logging.Level;
import java.util.logging.Logger;
@NullMarked
public class InternalAccessor implements Accessor {
protected final Logger logger;
private final PlayerManager manager;
protected final InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory;
private final AnySilentContainer anySilentContainer;
public InternalAccessor(Logger logger, LanguageManager lang) {
this.logger = logger;
factory = createInventoryFactory();
manager = createPlayerManager(logger, factory);
anySilentContainer = createAnySilentContainer(logger, lang);
}
protected InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> createInventoryFactory() {
return new OpenInventoryFactory();
}
protected PlayerManager createPlayerManager(
Logger logger,
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory
) {
return new PlayerManager(logger, factory);
}
protected AnySilentContainer createAnySilentContainer(
Logger logger,
LanguageManager lang
) {
return new AnySilentContainer(logger, lang);
}
protected PlaceholderLoader createPlaceholderLoader() {
return new PlaceholderLoader();
}
@Override
public PlayerManager getPlayerManager() {
return manager;
}
@Override
public IAnySilentContainer getAnySilentContainer() {
return anySilentContainer;
}
@Override
public ISpecialPlayerInventory createPlayerInventory(Player player) {
return new OpenInventory(factory, player);
}
@Override
public ISpecialEnderChest createEnderChest(Player player) {
return new OpenEnderChest(player);
}
@Override
public <T extends ISpecialInventory> @Nullable T get(Inventory bukkitInventory, Class<T> clazz) {
if (!(bukkitInventory instanceof CraftInventory craftInventory)) {
return null;
}
Container container = craftInventory.getInventory();
if (clazz.isInstance(container)) {
return clazz.cast(container);
}
return null;
}
@Override
public void reload(ConfigurationSection config) {
ConfigurationSection placeholders = config.getConfigurationSection("placeholders");
try {
// Reset placeholders to defaults and try to load configuration.
createPlaceholderLoader().load(placeholders);
} catch (Exception e) {
logger.log(Level.WARNING, "Caught exception loading placeholder overrides!", e);
}
}
}
@@ -0,0 +1,233 @@
/*
* 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.paper26_3.container;
import com.github.jikoo.openinv.internal.container.AnySilentContainerBase;
import com.lishid.openinv.internal.paper26_3.container.menu.OpenChestMenu;
import com.lishid.openinv.internal.paper26_3.player.PlayerManager;
import com.github.jikoo.openinv.util.ReflectionHelper;
import com.github.jikoo.openinv.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.entity.Entity;
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.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.logging.Logger;
@NullMarked
public class AnySilentContainer extends AnySilentContainerBase {
private final Logger logger;
private final LanguageManager lang;
private @Nullable Field serverPlayerGameModeGameType;
public AnySilentContainer(Logger logger, 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(
final Player bukkitPlayer,
final boolean silentchest,
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 = ((Entity) 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 @Nullable 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 (hasLootTable(lootable)) {
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;
}
protected boolean hasLootTable(RandomizableContainerBlockEntity lootable) {
return lootable.getLootTable() != null;
}
@Override
public void deactivateContainer(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);
}
}
@Override
protected org.bukkit.block.BlockState getState(org.bukkit.block.Block block) {
return block.getState(false);
}
@Override
protected @Nullable InventoryHolder getHolder(Inventory inventory) {
return inventory.getHolder(false);
}
}
@@ -0,0 +1,313 @@
package com.lishid.openinv.internal.paper26_3.container;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.github.jikoo.openinv.internal.container.InternalOwned;
import com.github.jikoo.openinv.internal.container.slot.Content;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.bukkit.OpenPlayerInventory;
import com.lishid.openinv.internal.paper26_3.container.menu.OpenChestMenu;
import com.lishid.openinv.internal.paper26_3.container.menu.OpenInventoryMenu;
import com.lishid.openinv.internal.paper26_3.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.paper26_3.player.PlayerManager;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
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.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.Location;
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.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@NullMarked
public abstract class BaseOpenInventory implements Container, InternalOwned<ServerPlayer>, ISpecialPlayerInventory {
protected final InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory;
protected final List<Content<ServerPlayer, ItemStack, Container, Slot>> slots;
private final int size;
protected ServerPlayer owner;
private int maxStackSize = 99;
protected @Nullable CraftInventory bukkitEntity;
public List<HumanEntity> transaction = new ArrayList<>();
public BaseOpenInventory(
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
org.bukkit.entity.Player bukkitPlayer
) {
this.factory = factory;
owner = PlayerManager.getHandle(bukkitPlayer);
// Get total size, rounding up to nearest 9 for client compatibility.
int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1;
size = ((int) Math.ceil(rawSize / 9.0)) * 9;
slots = NonNullList.withSize(size, factory.newViewOnly(owner));
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.getNonEquipmentItems().size() == 36
&& owner.inventoryMenu.getCraftSlots().getContainerSize() == 4
&& (Inventory.EQUIPMENT_SLOT_MAPPING.size() == 5 || Inventory.EQUIPMENT_SLOT_MAPPING.size() == 7)) {
// Armor slots: Bottom left.
addArmor(36);
// Off-hand: Below chestplate.
addOffHand(46);
// Drop slot: Bottom right.
slots.set(53, factory.newDrop(owner));
// Cursor slot: Above drop.
slots.set(44, factory.newCursor(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, factory.newCursor(owner));
// Drop slot last.
slots.set(slots.size() - 1, factory.newDrop(owner));
}
private int addMainInventory() {
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;
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,
factory.newList(owner, invIndex, type, holder -> holder.getInventory().getNonEquipmentItems())
);
}
return listSize;
}
private int addArmor(int startIndex) {
// 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. Also skip non-player slots.
if (sorted[i].getType() != EquipmentSlot.Type.HUMANOID_ARMOR) {
continue;
}
slots.set(startIndex + localIndex, factory.newEquipment(owner, sorted[i]));
++localIndex;
}
return startIndex + localIndex;
}
private int addOffHand(int startIndex) {
// No off-hand?
if (!Inventory.EQUIPMENT_SLOT_MAPPING.containsValue(EquipmentSlot.OFFHAND)) {
return startIndex;
}
slots.set(startIndex, factory.newOffHand(owner));
return startIndex + 1;
}
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, factory.newCrafting(owner, localIndex));
}
if (pretty) {
slots.set(startIndex + 2, factory.newViewOnly(owner, Placeholders.craftingOutput));
slots.set(startIndex + 11, factory.newCraftingResult(owner));
}
return startIndex + listSize;
}
public Slot getMenuSlot(int index, int x, int y) {
return slots.get(index).asSlot(this, index, x, y);
}
public InventoryType.SlotType getSlotType(int index) {
return slots.get(index).getSlotType();
}
public abstract Component getTitle(ServerPlayer player, @Nullable OpenChestMenu<?> menu);
@Override
public ServerPlayer getOwnerHandle() {
return owner;
}
@Override
public org.bukkit.inventory.Inventory getBukkitInventory() {
if (bukkitEntity == null) {
bukkitEntity = new OpenPlayerInventory(this);
}
return bukkitEntity;
}
@Override
public void setPlayerOnline(org.bukkit.entity.Player player) {
ServerPlayer newOwner = PlayerManager.getHandle(player);
// Only transfer regular inventory - crafting and cursor slots are transient.
newOwner.getInventory().replaceWith(owner.getInventory());
owner = newOwner;
// Update slots to point to new inventory.
slots.forEach(slot -> slot.setHolder(newOwner));
}
@Override
public boolean isInUse() {
return !transaction.isEmpty();
}
@Override
public org.bukkit.entity.Player getPlayer() {
return getOwner();
}
@Override
public int getContainerSize() {
return size;
}
@Override
public boolean isEmpty() {
return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty);
}
@Override
public ItemStack getItem(int index) {
return slots.get(index).get();
}
@Override
public ItemStack removeItem(int index, int amount) {
return slots.get(index).removePartial(amount);
}
@Override
public ItemStack removeItemNoUpdate(int index) {
return slots.get(index).remove();
}
@Override
public void setItem(int index, ItemStack itemStack) {
slots.get(index).set(itemStack);
}
@Override
public int getMaxStackSize() {
return maxStackSize;
}
@Override
public void setMaxStackSize(int maxStackSize) {
this.maxStackSize = maxStackSize;
}
@Override
public void setChanged() {
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public List<ItemStack> getContents() {
NonNullList<ItemStack> contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY);
for (int i = 0; i < getContainerSize(); ++i) {
contents.set(i, getItem(i));
}
return contents;
}
@Override
public void onOpen(CraftHumanEntity viewer) {
transaction.add(viewer);
}
@Override
public void onClose(CraftHumanEntity viewer) {
transaction.remove(viewer);
}
@Override
public List<HumanEntity> getViewers() {
return transaction;
}
@Override
public org.bukkit.entity.Player getOwner() {
return owner.getBukkitEntity();
}
@Override
public Location getLocation() {
return owner.getBukkitEntity().getLocation();
}
@Override
public void clearContent() {
owner.getInventory().clearContent();
owner.inventoryMenu.getCraftSlots().clearContent();
owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots());
owner.containerMenu.setCarried(ItemStack.EMPTY);
}
public @Nullable OpenChestMenu<?> createMenu(Player player, int i, boolean viewOnly) {
if (player instanceof ServerPlayer serverPlayer) {
return new OpenInventoryMenu(factory, this, serverPlayer, i, viewOnly);
}
return null;
}
}
@@ -0,0 +1,214 @@
package com.lishid.openinv.internal.paper26_3.container;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.github.jikoo.openinv.internal.container.InternalOwned;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.menu.OpenChestMenu;
import com.lishid.openinv.internal.paper26_3.container.menu.OpenEnderChestMenu;
import com.lishid.openinv.internal.paper26_3.player.PlayerManager;
import net.minecraft.core.NonNullList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedItemContents;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.inventory.StackedContentsCompatible;
import net.minecraft.world.item.ItemStack;
import org.bukkit.Location;
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.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@NullMarked
public class OpenEnderChest implements Container, StackedContentsCompatible, InternalOwned<ServerPlayer>,
ISpecialEnderChest {
private @Nullable CraftInventory inventory;
private ServerPlayer owner;
private NonNullList<ItemStack> items;
private int maxStack = 64;
private final List<HumanEntity> transaction = new ArrayList<>();
public OpenEnderChest(org.bukkit.entity.Player player) {
this.owner = PlayerManager.getHandle(player);
this.items = getEnderChestItems(owner);
}
@Override
public ServerPlayer getOwnerHandle() {
return owner;
}
@Override
public org.bukkit.inventory.Inventory getBukkitInventory() {
if (inventory == null) {
inventory = new CraftInventory(this) {
@Override
public InventoryType getType() {
return InventoryType.ENDER_CHEST;
}
};
}
return inventory;
}
@Override
public void setPlayerOnline(org.bukkit.entity.Player player) {
owner = PlayerManager.getHandle(player);
NonNullList<ItemStack> activeItems = getEnderChestItems(owner);
// Guard against size changing. Theoretically on Purpur all row variations still have 6 rows internally.
int max = Math.min(items.size(), activeItems.size());
for (int index = 0; index < max; ++index) {
activeItems.set(index, items.get(index));
}
items = activeItems;
}
protected NonNullList<ItemStack> getEnderChestItems(ServerPlayer owner) {
return owner.getEnderChestInventory().getItems();
}
@Override
public org.bukkit.entity.Player getPlayer() {
return owner.getBukkitEntity();
}
@Override
public int getContainerSize() {
return items.size();
}
@Override
public boolean isEmpty() {
return items.stream().allMatch(ItemStack::isEmpty);
}
@Override
public ItemStack getItem(int index) {
return index >= 0 && index < items.size() ? items.get(index) : ItemStack.EMPTY;
}
@Override
public ItemStack removeItem(int index, int amount) {
ItemStack itemstack = ContainerHelper.removeItem(items, index, amount);
if (!itemstack.isEmpty()) {
setChanged();
}
return itemstack;
}
@Override
public ItemStack removeItemNoUpdate(int index) {
return index >= 0 && index < items.size() ? items.set(index, ItemStack.EMPTY) : ItemStack.EMPTY;
}
@Override
public void setItem(int index, ItemStack itemStack) {
if (index >= 0 && index < items.size()) {
items.set(index, itemStack);
}
}
@Override
public int getMaxStackSize() {
return maxStack;
}
@Override
public void setChanged() {
this.owner.getEnderChestInventory().setChanged();
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public List<ItemStack> getContents() {
return items;
}
@Override
public void onOpen(CraftHumanEntity craftHumanEntity) {
transaction.add(craftHumanEntity);
}
@Override
public void onClose(CraftHumanEntity craftHumanEntity) {
transaction.remove(craftHumanEntity);
}
@Override
public List<HumanEntity> getViewers() {
return transaction;
}
@Override
public org.bukkit.entity.Player getOwner() {
return getPlayer();
}
@Override
public void setMaxStackSize(int size) {
maxStack = size;
}
@Override
public @Nullable Location getLocation() {
return null;
}
@Override
public void clearContent() {
items.clear();
setChanged();
}
@Override
public void fillStackedContents(StackedItemContents stackedContents) {
for (ItemStack itemstack : items) {
stackedContents.accountStack(itemstack);
}
}
public Component getTitle(@Nullable OpenChestMenu<?> menu) {
MutableComponent component;
if (menu != null && menu.isViewOnly()) {
component = Component.translatableWithFallback("openinv.container.enderchest.viewonly", "[RO] ");
} else {
component = Component.translatableWithFallback("openinv.container.enderchest.editable", "");
}
return component
.append(Component.translatableWithFallback("openinv.container.enderchest.prefix", "", owner.getName()))
.append(Component.translatable("container.enderchest"))
.append(Component.translatableWithFallback("openinv.container.enderchest.suffix", " - %s", owner.getName()));
}
public @Nullable OpenChestMenu<?> createMenu(
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
Player player,
int i,
boolean viewOnly
) {
if (player instanceof ServerPlayer serverPlayer) {
return new OpenEnderChestMenu(factory, this, serverPlayer, i, viewOnly);
}
return null;
}
}
@@ -0,0 +1,58 @@
package com.lishid.openinv.internal.paper26_3.container;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.menu.OpenChestMenu;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FontDescription;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.resources.Identifier;
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.entity.Player;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class OpenInventory extends BaseOpenInventory {
public OpenInventory(
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
Player bukkitPlayer
) {
super(factory, bukkitPlayer);
}
@Override
public Component getTitle(@Nullable ServerPlayer viewer, @Nullable OpenChestMenu<?> menu) {
MutableComponent component = Component.empty();
// Prefix for use with custom bitmap image fonts.
if (owner.equals(viewer)) {
component.append(
Component.translatableWithFallback("openinv.container.inventory.self", "")
.withStyle(style -> style
.withFont(new FontDescription.Resource(Identifier.parse("openinv:font/inventory")))
.withColor(ChatFormatting.WHITE)));
} else {
component.append(
Component.translatableWithFallback("openinv.container.inventory.other", "")
.withStyle(style -> style
.withFont(new FontDescription.Resource(Identifier.parse("openinv:font/inventory")))
.withColor(ChatFormatting.WHITE)));
}
if (menu != null && menu.isViewOnly()) {
component.append(Component.translatableWithFallback("openinv.container.inventory.viewonly", "[RO] "));
} else {
component.append(Component.translatableWithFallback("openinv.container.inventory.editable", ""));
}
// Normal title: "Inventory - OwnerName"
component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName()))
.append(Component.translatable("container.inventory"))
.append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName()));
return component;
}
}
@@ -0,0 +1,174 @@
package com.lishid.openinv.internal.paper26_3.container.bukkit;
import com.github.jikoo.openinv.internal.container.ViewOnly;
import net.minecraft.world.Container;
import org.bukkit.Material;
import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.Collections;
import java.util.HashMap;
import java.util.ListIterator;
/**
* A locked down "empty" inventory that rejects plugin interaction.
*/
@NullMarked
public class OpenDummyInventory extends CraftInventory implements ViewOnly {
private final InventoryType type;
public OpenDummyInventory(Container inventory, InventoryType type) {
super(inventory);
this.type = type;
}
@Override
public InventoryType getType() {
return type;
}
@Override
public @Nullable ItemStack getItem(int index) {
return null;
}
@Override
public void setItem(int index, @Nullable ItemStack item) {
}
@SuppressWarnings("NonApiType")
@Override
public HashMap<Integer, ItemStack> addItem(ItemStack... items) throws IllegalArgumentException {
return arrayToHashMap(items);
}
@SuppressWarnings("NonApiType")
@Override
public HashMap<Integer, ItemStack> removeItem(ItemStack... items) throws IllegalArgumentException {
return arrayToHashMap(items);
}
@SuppressWarnings("NonApiType")
private static HashMap<Integer, ItemStack> arrayToHashMap(ItemStack[] items) {
HashMap<Integer, ItemStack> ignored = new HashMap<>();
for (int index = 0; index < items.length; ++index) {
ignored.put(index, items[index]);
}
return ignored;
}
@Override
public ItemStack[] getContents() {
return new ItemStack[getSize()];
}
@Override
public void setContents(ItemStack[] items) throws IllegalArgumentException {
}
@Override
public ItemStack[] getStorageContents() {
return new ItemStack[getSize()];
}
@Override
public void setStorageContents(ItemStack[] items) throws IllegalArgumentException {
}
@Override
public boolean contains(Material material) throws IllegalArgumentException {
return false;
}
@Override
public boolean contains(@Nullable ItemStack item) {
return false;
}
@Override
public boolean contains(Material material, int amount) throws IllegalArgumentException {
return false;
}
@Override
public boolean contains(@Nullable ItemStack item, int amount) {
return false;
}
@Override
public boolean containsAtLeast(@Nullable ItemStack item, int amount) {
return false;
}
@SuppressWarnings("NonApiType")
@Override
public HashMap<Integer, ItemStack> all(
Material material
) throws IllegalArgumentException {
return new HashMap<>();
}
@SuppressWarnings("NonApiType")
@Override
public HashMap<Integer, ItemStack> all(@Nullable ItemStack item) {
return new HashMap<>();
}
@Override
public int first(Material material) throws IllegalArgumentException {
return -1;
}
@Override
public int first(ItemStack item) {
return -1;
}
@Override
public int firstEmpty() {
return -1;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public void remove(Material material) throws IllegalArgumentException {
}
@Override
public void remove(ItemStack item) {
}
@Override
public void clear(int index) {
}
@Override
public void clear() {
}
@Override
public ListIterator<ItemStack> iterator() {
return Collections.emptyListIterator();
}
@Override
public ListIterator<ItemStack> iterator(int index) {
return Collections.emptyListIterator();
}
}
@@ -0,0 +1,137 @@
package com.lishid.openinv.internal.paper26_3.container.bukkit;
import net.minecraft.world.Container;
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.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
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 ItemStack[] getArmorContents() {
return new ItemStack[4];
}
@Override
public ItemStack[] getExtraContents() {
return new ItemStack[4];
}
@Override
public ItemStack getHelmet() {
return ItemStack.empty();
}
@Override
public ItemStack getChestplate() {
return ItemStack.empty();
}
@Override
public ItemStack getLeggings() {
return ItemStack.empty();
}
@Override
public ItemStack getBoots() {
return ItemStack.empty();
}
@Override
public void setItem(EquipmentSlot slot, @Nullable ItemStack item) {
}
@Override
public ItemStack getItem(EquipmentSlot slot) {
return ItemStack.empty();
}
@Override
public void setArmorContents(@Nullable ItemStack[] items) {
}
@Override
public void setExtraContents(@Nullable ItemStack[] items) {
}
@Override
public void setHelmet(@Nullable ItemStack helmet) {
}
@Override
public void setChestplate(@Nullable ItemStack chestplate) {
}
@Override
public void setLeggings(@Nullable ItemStack leggings) {
}
@Override
public void setBoots(@Nullable ItemStack boots) {
}
@Override
public ItemStack getItemInMainHand() {
return ItemStack.empty();
}
@Override
public void setItemInMainHand(@Nullable ItemStack item) {
}
@Override
public ItemStack getItemInOffHand() {
return ItemStack.empty();
}
@Override
public void setItemInOffHand(@Nullable ItemStack item) {
}
@SuppressWarnings("InlineMeSuggester")
@Deprecated
@Override
public ItemStack getItemInHand() {
return ItemStack.empty();
}
@Deprecated
@Override
public void setItemInHand(@Nullable ItemStack stack) {
}
@Override
public int getHeldItemSlot() {
return 0;
}
@Override
public void setHeldItemSlot(int slot) {
}
}
@@ -0,0 +1,218 @@
package com.lishid.openinv.internal.paper26_3.container.bukkit;
import com.google.common.base.Preconditions;
import com.lishid.openinv.internal.paper26_3.container.BaseOpenInventory;
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.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.List;
@NullMarked
public class OpenPlayerInventory extends CraftInventory implements PlayerInventory {
public OpenPlayerInventory(BaseOpenInventory inventory) {
super(inventory);
}
@Override
public BaseOpenInventory getInventory() {
return (BaseOpenInventory) super.getInventory();
}
@Override
public ItemStack[] getContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getContents());
}
@Override
public void setContents(@Nullable ItemStack[] items) {
Inventory internal = getInventory().getOwnerHandle().getInventory();
int size = internal.getContainerSize();
Preconditions.checkArgument(items.length <= size, "items.length must be <= %s", size);
for (int index = 0; index < size; ++index) {
if (index < items.length) {
internal.setItem(index, CraftItemStack.asNMSCopy(items[index]));
} else {
internal.setItem(index, net.minecraft.world.item.ItemStack.EMPTY);
}
}
}
@Override
public ItemStack[] getStorageContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getNonEquipmentItems());
}
@Override
public void setStorageContents(@Nullable 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 InventoryType getType() {
return InventoryType.PLAYER;
}
@Override
public Player getHolder() {
return getInventory().getOwner();
}
@Override
public ItemStack[] getArmorContents() {
return asCraftMirror(getInventory().getOwnerHandle().getInventory().getArmorContents());
}
@Override
public void setArmorContents(@Nullable ItemStack[] 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 ItemStack[] getExtraContents() {
return asCraftMirror(List.of(getInventory().getOwnerHandle().getInventory().equipment.get(EquipmentSlot.OFFHAND)));
}
@Override
public void setExtraContents(@Nullable ItemStack[] 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 ItemStack getHelmet() {
return getInventory().getOwner().getInventory().getHelmet();
}
@Override
public void setHelmet(@Nullable ItemStack helmet) {
getInventory().getOwner().getInventory().setHelmet(helmet);
}
@Override
public ItemStack getChestplate() {
return getInventory().getOwner().getInventory().getChestplate();
}
@Override
public void setChestplate(@Nullable ItemStack chestplate) {
getInventory().getOwner().getInventory().setChestplate(chestplate);
}
@Override
public ItemStack getLeggings() {
return getInventory().getOwner().getInventory().getLeggings();
}
@Override
public void setLeggings(@Nullable ItemStack leggings) {
getInventory().getOwner().getInventory().setLeggings(leggings);
}
@Override
public ItemStack getBoots() {
return getInventory().getOwner().getInventory().getBoots();
}
@Override
public void setBoots(@Nullable ItemStack boots) {
getInventory().getOwner().getInventory().setBoots(boots);
}
@Override
public ItemStack getItemInMainHand() {
return getInventory().getOwner().getInventory().getItemInMainHand();
}
@Override
public void setItemInMainHand(@Nullable ItemStack item) {
getInventory().getOwner().getInventory().setItemInMainHand(item);
}
@Override
public ItemStack getItemInOffHand() {
return getInventory().getOwner().getInventory().getItemInOffHand();
}
@Override
public void setItemInOffHand(@Nullable ItemStack item) {
getInventory().getOwner().getInventory().setItemInOffHand(item);
}
@SuppressWarnings("InlineMeSuggester")
@Deprecated
@Override
public ItemStack getItemInHand() {
return getItemInMainHand();
}
@SuppressWarnings("InlineMeSuggester")
@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 ItemStack getItem(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(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);
}
}
}
@@ -0,0 +1,28 @@
package com.lishid.openinv.internal.paper26_3.container.bukkit;
import com.lishid.openinv.internal.paper26_3.container.BaseOpenInventory;
import org.bukkit.inventory.ItemStack;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class OpenPlayerInventorySelf extends OpenPlayerInventory {
private final int offset;
public OpenPlayerInventorySelf(BaseOpenInventory inventory, int offset) {
super(inventory);
this.offset = offset;
}
@Override
public @Nullable ItemStack getItem(int index) {
return super.getItem(offset + index);
}
@Override
public void setItem(int index, @Nullable ItemStack item) {
super.setItem(offset + index, item);
}
}
@@ -0,0 +1,273 @@
package com.lishid.openinv.internal.paper26_3.container.menu;
import com.google.common.base.Preconditions;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.BaseOpenInventory;
import com.lishid.openinv.internal.paper26_3.container.bukkit.OpenDummyPlayerInventory;
import com.lishid.openinv.internal.paper26_3.container.bukkit.OpenPlayerInventorySelf;
import com.lishid.openinv.internal.paper26_3.container.slot.ContentDrop;
import com.lishid.openinv.internal.paper26_3.container.slot.SlotEquipment;
import com.lishid.openinv.internal.paper26_3.container.slot.SlotViewOnly;
import com.lishid.openinv.util.Permissions;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
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.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class BaseOpenInventoryMenu extends OpenSyncMenu<BaseOpenInventory> {
private int offset;
public BaseOpenInventoryMenu(
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
BaseOpenInventory inventory,
ServerPlayer viewer,
int i,
boolean viewOnly
) {
super(getMenuType(inventory, viewer), i, factory, inventory, viewer, viewOnly);
}
private static MenuType<ChestMenu> getMenuType(BaseOpenInventory 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 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 SlotEquipment equipment) {
if (viewOnly) {
return factory.wrapViewOnly(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 factory.wrapViewOnly(slot);
}
return slot;
}
@Override
protected CraftInventoryView<OpenChestMenu<BaseOpenInventory>, 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.@Nullable ItemStack getItem(int index) {
if (viewOnly || index < 0) {
return null;
}
Slot slot = slots.get(index);
return factory.asBukkitMirror(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 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 BaseOpenInventoryMenu.this.container.getSlotType(offset + slot);
}
@Override
public int countSlots() {
return topSize + BOTTOM_INVENTORY_SIZE;
}
};
}
@Override
public ItemStack quickMoveStack(Player player, int index) {
if (viewOnly) {
return ItemStack.EMPTY;
}
// See ChestMenu and InventoryMenu
Slot slot = this.slots.get(index);
if (!slot.hasItem() || slot.isFake()) {
return ItemStack.EMPTY;
}
ItemStack itemStack = slot.getItem();
ItemStack originalStack = itemStack.copy();
if (index < topSize) {
// If we're moving top to bottom, do a normal transfer.
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
return ItemStack.EMPTY;
}
} else {
EquipmentSlot equipmentSlot = player.getEquipmentSlotForItem(itemStack);
boolean movedGear = switch (equipmentSlot) {
// If this is gear, try to move it to the correct slot first.
case OFFHAND, FEET, LEGS, CHEST, HEAD -> {
// Locate the correct slot in the contents following the main inventory.
for (int extra = container.getOwnerHandle().getInventory().getNonEquipmentItems().size() - offset; extra < topSize; ++extra) {
Slot extraSlot = getSlot(extra);
if (extraSlot instanceof 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;
}
}
@@ -0,0 +1,281 @@
package com.lishid.openinv.internal.paper26_3.container.menu;
import com.lishid.openinv.internal.ISpecialInventory;
import com.github.jikoo.openinv.internal.container.InternalOwned;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.bukkit.OpenDummyInventory;
import com.lishid.openinv.internal.paper26_3.container.slot.SlotViewOnly;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
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.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* An extension of {@link AbstractContainerMenu} storing and managing data common to all special inventories.
*/
@NullMarked
public abstract class OpenChestMenu<T extends Container & ISpecialInventory & InternalOwned<ServerPlayer>>
extends AbstractContainerMenu {
protected static final int BOTTOM_INVENTORY_SIZE = 36;
protected final InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory;
protected final T container;
protected final ServerPlayer viewer;
protected final boolean viewOnly;
protected final boolean ownContainer;
protected final int topSize;
private @Nullable CraftInventoryView<OpenChestMenu<T>, Inventory> bukkitEntity;
protected OpenChestMenu(
MenuType<ChestMenu> type,
int containerCounter,
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
T container,
ServerPlayer viewer,
boolean viewOnly
) {
super(type, containerCounter);
this.factory = factory;
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 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 Slot getUpperSlot(int index, int x, int y) {
Slot slot = new Slot(container, index, x, y);
if (viewOnly) {
return factory.wrapViewOnly(slot);
}
return slot;
}
public boolean isViewOnly() {
return viewOnly;
}
@Override
public final CraftInventoryView<OpenChestMenu<T>, Inventory> getBukkitView() {
if (bukkitEntity == null) {
bukkitEntity = createBukkitEntity();
}
return bukkitEntity;
}
protected 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 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.GENERIC_9x1.equals(menuType)) {
return 9;
} else if (MenuType.GENERIC_9x2.equals(menuType)) {
return 18;
} else if (MenuType.GENERIC_9x3.equals(menuType)) {
return 27;
} else if (MenuType.GENERIC_9x4.equals(menuType)) {
return 36;
} else if (MenuType.GENERIC_9x5.equals(menuType)) {
return 45;
} else if (MenuType.GENERIC_9x6.equals(menuType)) {
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 boolean stillValid(Player player) {
return true;
}
}
@@ -0,0 +1,70 @@
package com.lishid.openinv.internal.paper26_3.container.menu;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.OpenEnderChest;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ContainerInput;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class OpenEnderChestMenu extends OpenSyncMenu<OpenEnderChest> {
public OpenEnderChestMenu(
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
OpenEnderChest enderChest,
ServerPlayer viewer,
int containerId,
boolean viewOnly
) {
super(getChestMenuType(enderChest.getContainerSize()), containerId, factory, enderChest, viewer, viewOnly);
}
@Override
public ItemStack quickMoveStack(Player player, int index) {
if (viewOnly) {
return ItemStack.EMPTY;
}
// See ChestMenu
Slot slot = this.slots.get(index);
if (slot.isFake() || !slot.hasItem()) {
return ItemStack.EMPTY;
}
ItemStack itemStack = slot.getItem();
ItemStack original = itemStack.copy();
if (index < topSize) {
if (!this.moveItemStackTo(itemStack, topSize, this.slots.size(), true)) {
return ItemStack.EMPTY;
}
} else if (!this.moveItemStackTo(itemStack, 0, topSize, false)) {
return ItemStack.EMPTY;
}
if (itemStack.isEmpty()) {
slot.setByPlayer(ItemStack.EMPTY);
} else {
slot.setChanged();
}
return original;
}
@Override
public void clicked(int slotIndex, int buttonNum, ContainerInput containerInput, Player player) {
if (viewOnly) {
if (containerInput == ContainerInput.QUICK_CRAFT) {
sendAllDataToRemote();
}
}
super.clicked(slotIndex, buttonNum, containerInput, player);
}
}
@@ -0,0 +1,37 @@
package com.lishid.openinv.internal.paper26_3.container.menu;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.BaseOpenInventory;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ContainerInput;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.NullMarked;
@NullMarked
public class OpenInventoryMenu extends BaseOpenInventoryMenu {
public OpenInventoryMenu(
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
BaseOpenInventory inventory,
ServerPlayer viewer,
int i,
boolean viewOnly
) {
super(factory, inventory, viewer, i, viewOnly);
}
@Override
public void clicked(int slotIndex, int buttonNum, ContainerInput containerInput, Player player) {
if (viewOnly) {
if (containerInput == ContainerInput.QUICK_CRAFT) {
sendAllDataToRemote();
}
}
super.clicked(slotIndex, buttonNum, containerInput, player);
}
}
@@ -0,0 +1,169 @@
package com.lishid.openinv.internal.paper26_3.container.menu;
import com.google.common.base.Suppliers;
import com.lishid.openinv.internal.ISpecialInventory;
import com.github.jikoo.openinv.internal.container.InternalOwned;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.slot.SlotPlaceholder;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ChestMenu;
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.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
/**
* An extension of {@link OpenChestMenu} that supports {@link SlotPlaceholder placeholders}.
*/
@NullMarked
public abstract class OpenSyncMenu<T extends Container & ISpecialInventory & InternalOwned<ServerPlayer>>
extends OpenChestMenu<T> {
// Syncher fields required to send placeholder items.
// All fields intentionally shadow AbstractContainerMenu fields so that warnings
// will be generated if they are no longer necessary.
protected @Nullable ContainerSynchronizer synchronizer;
protected final List<ContainerListener> listeners = new ArrayList<>();
private @Nullable RemoteSlot remoteCarried = RemoteSlot.PLACEHOLDER;
protected boolean suppressRemoteUpdates;
protected OpenSyncMenu(
MenuType<ChestMenu> type,
int containerCounter,
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory,
T container,
ServerPlayer viewer,
boolean viewOnly
) {
super(type, containerCounter, factory, container, viewer, viewOnly);
}
@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);
}
// See private synchronizeCarriedToRemote
if (!suppressRemoteUpdates && !getRemoteCarried().matches(getCarried())) {
this.broadcastCarriedItem();
}
for (int index = 0; index < this.dataSlots.size(); ++index) {
DataSlot dataSlot = this.dataSlots.get(index);
int current = dataSlot.get();
if (dataSlot.checkAndClearUpdateFlag()) {
// See private updateDataSlotListeners
for (ContainerListener containerListener : this.listeners) {
containerListener.dataChanged(this, index, current);
}
}
// See private synchronizeDataSlotToRemote
if (!suppressRemoteUpdates && remoteDataSlots.getInt(index) != current) {
remoteDataSlots.set(index, current);
if (synchronizer != null) {
synchronizer.sendDataChange(this, index, current);
}
}
}
}
@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.copy());
this.remoteSlots.get(index).force(itemStack);
}
getRemoteCarried().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());
this.synchronizer.sendOffHandSlotChange();
}
}
protected RemoteSlot getRemoteCarried() {
if (remoteCarried != null) {
return remoteCarried;
}
try {
Field remoteCarried = AbstractContainerMenu.class.getDeclaredField("remoteCarried");
remoteCarried.setAccessible(true);
Object slot = remoteCarried.get(this);
if (slot instanceof RemoteSlot remoteSlot) {
this.remoteCarried = remoteSlot;
} else {
this.remoteCarried = RemoteSlot.PLACEHOLDER;
}
} catch (NoSuchFieldException | IllegalAccessException e) {
remoteCarried = RemoteSlot.PLACEHOLDER;
}
return remoteCarried;
}
// Overrides from here on are purely to capture the contents of fields that aren't exposed.
// Note that super calls should likely always be last! Some send data to remotes after updating state.
@Override
public void addSlotListener(ContainerListener containerListener) {
if (!this.listeners.contains(containerListener)) {
this.listeners.add(containerListener);
}
super.addSlotListener(containerListener);
}
@Override
public void setSynchronizer(ContainerSynchronizer containerSynchronizer) {
this.synchronizer = containerSynchronizer;
// Unset carried slot so we re-capture it when sending slots.
// setSynchronizer calls sendAllDataToRemote, so we can't just do the reflection here once after the fact.
this.remoteCarried = null;
super.setSynchronizer(containerSynchronizer);
}
@Override
public void removeSlotListener(ContainerListener containerListener) {
this.listeners.remove(containerListener);
super.removeSlotListener(containerListener);
}
@Override
public void suppressRemoteUpdates() {
this.suppressRemoteUpdates = true;
super.suppressRemoteUpdates();
}
@Override
public void resumeRemoteUpdates() {
this.suppressRemoteUpdates = false;
super.resumeRemoteUpdates();
}
}
@@ -0,0 +1,131 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import com.github.jikoo.openinv.internal.container.slot.Content;
import com.lishid.openinv.internal.paper26_3.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.paper26_3.player.OpenPlayer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Prediction;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.jspecify.annotations.NullMarked;
import java.util.List;
/**
* A slot in a survival crafting inventory. Unavailable when not online in a survival mode.
*/
@NullMarked
public class ContentCrafting implements Content<ServerPlayer, ItemStack, Container, Slot> {
private final int index;
protected ServerPlayer holder;
protected List<ItemStack> items;
protected ContentCrafting(ServerPlayer holder, int index) {
setHolder(holder);
this.index = index;
}
public static boolean isAvailable(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 OpenPlayer.isConnected(holder.connection) && holder.gameMode.isSurvival();
}
@Override
public void setHolder(ServerPlayer holder) {
this.holder = holder;
// Note: CraftingContainer#getItems is immutable! Be careful with updates.
this.items = holder.inventoryMenu.getCraftSlots().getContents();
}
@Override
public ItemStack get() {
return isAvailable(holder) ? items.get(index) : ItemStack.EMPTY;
}
@Override
public ItemStack remove() {
if (!isAvailable(holder)) {
return ItemStack.EMPTY;
}
ItemStack removed = items.remove(index);
if (removed.isEmpty()) {
return ItemStack.EMPTY;
}
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
return removed;
}
@Override
public ItemStack removePartial(int amount) {
if (!isAvailable(holder)) {
return ItemStack.EMPTY;
}
ItemStack removed = ContainerHelper.removeItem(items, index, amount);
if (removed.isEmpty()) {
return ItemStack.EMPTY;
}
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
return removed;
}
@Override
public void set(ItemStack itemStack) {
if (isAvailable(holder)) {
items.set(index, itemStack);
holder.inventoryMenu.slotsChanged(holder.inventoryMenu.getCraftSlots());
} else {
this.holder.drop(itemStack, false, Prediction.SERVER_ONLY);
}
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotCrafting(container, slot, x, y);
}
@Override
public InventoryType.SlotType getSlotType() {
return isAvailable(holder) ? InventoryType.SlotType.CRAFTING : InventoryType.SlotType.OUTSIDE;
}
public class SlotCrafting extends SlotPlaceholder {
private SlotCrafting(Container container, int index, int x, int y) {
super(container, index, x, y);
}
@Override
public ItemStack getOrDefault() {
return isAvailable(holder) ? items.get(ContentCrafting.this.index) : Placeholders.survivalOnly(holder);
}
@Override
public boolean mayPickup(Player player) {
return isAvailable(holder);
}
@Override
public boolean mayPlace(ItemStack itemStack) {
return isAvailable(holder);
}
@Override
public boolean hasItem() {
return isAvailable(holder) && super.hasItem();
}
@Override
public boolean isFake() {
return !isAvailable(holder);
}
}
}
@@ -0,0 +1,49 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import com.lishid.openinv.internal.paper26_3.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.jspecify.annotations.NullMarked;
/**
* A slot allowing viewing of the crafting result.
*
* <p>Unmodifiable because I said so. Use your own crafting grid.</p>
*/
@NullMarked
class ContentCraftingResult extends ContentViewOnly {
ContentCraftingResult(ServerPlayer holder) {
super(holder);
}
@Override
public ItemStack get() {
InventoryMenu inventoryMenu = holder.inventoryMenu;
return inventoryMenu.getResultSlot().getItem();
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotViewOnly(container, slot, x, y) {
@Override
public ItemStack getOrDefault() {
if (!ContentCrafting.isAvailable(holder)) {
return Placeholders.survivalOnly(holder);
}
InventoryMenu inventoryMenu = holder.inventoryMenu;
return inventoryMenu.getResultSlot().getItem();
}
};
}
@Override
public InventoryType.SlotType getSlotType() {
return InventoryType.SlotType.RESULT;
}
}
@@ -0,0 +1,121 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import com.github.jikoo.openinv.internal.container.slot.Content;
import com.lishid.openinv.internal.paper26_3.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.paper26_3.player.OpenPlayer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Prediction;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.jspecify.annotations.NullMarked;
/**
* A slot wrapping the active menu's cursor. Unavailable when not online in a survival mode.
*/
@NullMarked
public class ContentCursor implements Content<ServerPlayer, ItemStack, Container, Slot> {
protected ServerPlayer holder;
protected ContentCursor(ServerPlayer holder) {
this.holder = holder;
}
@Override
public void setHolder(ServerPlayer holder) {
this.holder = holder;
}
@Override
public ItemStack get() {
return isAvailable() ? holder.containerMenu.getCarried() : ItemStack.EMPTY;
}
@Override
public ItemStack remove() {
ItemStack carried = holder.containerMenu.getCarried();
holder.containerMenu.setCarried(ItemStack.EMPTY);
return carried;
}
@Override
public ItemStack removePartial(int amount) {
ItemStack carried = holder.containerMenu.getCarried();
if (!carried.isEmpty() && carried.getCount() >= amount) {
ItemStack value = carried.split(amount);
if (carried.isEmpty()) {
holder.containerMenu.setCarried(ItemStack.EMPTY);
}
return value;
}
return ItemStack.EMPTY;
}
@Override
public void set(ItemStack itemStack) {
if (isAvailable()) {
holder.containerMenu.setCarried(itemStack);
} else {
this.holder.drop(itemStack, false, Prediction.SERVER_ONLY);
}
}
protected boolean isAvailable() {
// Player must be online and not in creative - since the creative client is (semi-)authoritative,
// it ignores changes without extra help, and will delete the item as a result.
// Spectator mode is technically possible but may cause the item to be dropped if the client opens an inventory.
return OpenPlayer.isConnected(holder.connection) && holder.gameMode.isSurvival();
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotCursor(container, slot, x, y);
}
@Override
public InventoryType.SlotType getSlotType() {
// As close as possible to "not real"
return InventoryType.SlotType.OUTSIDE;
}
public class SlotCursor extends SlotPlaceholder {
private SlotCursor(Container container, int index, int x, int y) {
super(container, index, x, y);
}
@Override
public ItemStack getOrDefault() {
if (!isAvailable()) {
return Placeholders.survivalOnly(holder);
}
ItemStack carried = holder.containerMenu.getCarried();
return carried.isEmpty() ? Placeholders.cursor : carried;
}
@Override
public boolean mayPickup(Player player) {
return isAvailable();
}
@Override
public boolean mayPlace(ItemStack itemStack) {
return isAvailable();
}
@Override
public boolean hasItem() {
return isAvailable() && super.hasItem();
}
@Override
public boolean isFake() {
return true;
}
}
}
@@ -0,0 +1,92 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import com.github.jikoo.openinv.internal.container.slot.Content;
import com.lishid.openinv.internal.paper26_3.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.paper26_3.player.OpenPlayer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Prediction;
import net.minecraft.world.Container;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.jspecify.annotations.NullMarked;
/**
* A fake slot used to drop items. Unavailable offline.
*/
@NullMarked
public class ContentDrop implements Content<ServerPlayer, ItemStack, Container, Slot> {
protected ServerPlayer holder;
protected ContentDrop(ServerPlayer holder) {
this.holder = holder;
}
@Override
public void setHolder(ServerPlayer holder) {
this.holder = holder;
}
@Override
public ItemStack get() {
return ItemStack.EMPTY;
}
@Override
public ItemStack remove() {
return ItemStack.EMPTY;
}
@Override
public ItemStack removePartial(int amount) {
return ItemStack.EMPTY;
}
@Override
public void set(ItemStack itemStack) {
this.holder.drop(itemStack, false, Prediction.SERVER_ONLY);
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotDrop(container, slot, x, y);
}
@Override
public InventoryType.SlotType getSlotType() {
// Behaves like dropping an item outside the screen, just by the target player.
return InventoryType.SlotType.OUTSIDE;
}
public class SlotDrop extends SlotPlaceholder {
private SlotDrop(Container container, int index, int x, int y) {
super(container, index, x, y);
}
@Override
public ItemStack getOrDefault() {
return OpenPlayer.isConnected(holder.connection)
? Placeholders.drop
: Placeholders.blockedOffline;
}
@Override
public boolean mayPlace(ItemStack itemStack) {
return OpenPlayer.isConnected(holder.connection);
}
@Override
public boolean hasItem() {
return false;
}
@Override
public boolean isFake() {
return true;
}
}
}
@@ -0,0 +1,75 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import com.github.jikoo.openinv.internal.container.slot.Content;
import com.lishid.openinv.internal.paper26_3.container.slot.placeholder.Placeholders;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
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.jspecify.annotations.NullMarked;
/**
* A slot for equipment that displays placeholders if empty.
*/
@NullMarked
class ContentEquipment implements Content<ServerPlayer, ItemStack, Container, Slot> {
private EntityEquipment equipment;
private final ItemStack placeholder;
private final EquipmentSlot equipmentSlot;
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(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, placeholder, equipmentSlot);
}
@Override
public InventoryType.SlotType getSlotType() {
return InventoryType.SlotType.ARMOR;
}
}
@@ -0,0 +1,68 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import com.github.jikoo.openinv.internal.container.slot.Content;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.jspecify.annotations.NullMarked;
import java.util.List;
/**
* A normal slot backed by an item list.
*/
@NullMarked
abstract class ContentList implements Content<ServerPlayer, ItemStack, Container, Slot> {
private final int index;
private final InventoryType.SlotType slotType;
private List<ItemStack> items;
ContentList(ServerPlayer holder, int index, InventoryType.SlotType slotType) {
this.index = index;
this.slotType = slotType;
setHolder(holder);
}
@Override
public void setHolder(ServerPlayer holder) {
this.items = getItems(holder);
}
protected abstract List<ItemStack> getItems(ServerPlayer holder);
@Override
public ItemStack get() {
return items.get(index);
}
@Override
public ItemStack remove() {
ItemStack removed = items.remove(index);
return removed.isEmpty() ? ItemStack.EMPTY : removed;
}
@Override
public ItemStack removePartial(int amount) {
return ContainerHelper.removeItem(items, index, amount);
}
@Override
public void set(ItemStack itemStack) {
items.set(index, itemStack);
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new Slot(container, slot, x, y);
}
@Override
public InventoryType.SlotType getSlotType() {
return slotType;
}
}
@@ -0,0 +1,58 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import com.lishid.openinv.internal.paper26_3.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.paper26_3.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.jspecify.annotations.NullMarked;
import java.util.Objects;
/**
* A slot for equipment that updates held items if necessary.
*/
@NullMarked
class ContentOffHand extends ContentEquipment {
private ServerPlayer holder;
ContentOffHand(ServerPlayer holder) {
super(holder, EquipmentSlot.OFFHAND);
this.holder = holder; // Redundant, but silences nullity warning.
}
@Override
public void setHolder(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, Placeholders.emptyOffHand, EquipmentSlot.OFFHAND) {
@Override
public void setChanged() {
if (OpenPlayer.isConnected(holder.connection) && !Objects.equals(holder.containerMenu, holder.inventoryMenu)) {
holder.connection.send(
new ClientboundContainerSetSlotPacket(
holder.inventoryMenu.containerId,
holder.inventoryMenu.incrementStateId(),
InventoryMenu.SHIELD_SLOT,
holder.getOffhandItem()
));
}
}
};
}
}
@@ -0,0 +1,59 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import com.github.jikoo.openinv.internal.container.slot.Content;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.Prediction;
import net.minecraft.world.Container;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.jspecify.annotations.NullMarked;
/**
* A view-only slot that can't be interacted with.
*/
@NullMarked
public class ContentViewOnly implements Content<ServerPlayer, ItemStack, Container, Slot> {
protected ServerPlayer holder;
protected ContentViewOnly(ServerPlayer holder) {
this.holder = holder;
}
@Override
public void setHolder(ServerPlayer holder) {
this.holder = holder;
}
@Override
public ItemStack get() {
return ItemStack.EMPTY;
}
@Override
public ItemStack remove() {
return ItemStack.EMPTY;
}
@Override
public ItemStack removePartial(int amount) {
return ItemStack.EMPTY;
}
@Override
public void set(ItemStack itemStack) {
this.holder.drop(itemStack, false, Prediction.SERVER_ONLY);
}
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotViewOnly(container, slot, x, y);
}
@Override
public InventoryType.SlotType getSlotType() {
return InventoryType.SlotType.OUTSIDE;
}
}
@@ -0,0 +1,115 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import com.github.jikoo.openinv.internal.container.slot.Content;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
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.inventory.CraftItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.jspecify.annotations.NullMarked;
import java.util.List;
import java.util.function.Function;
@NullMarked
public class OpenInventoryFactory implements InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> {
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newCrafting(ServerPlayer holder, int index) {
return new ContentCrafting(holder, index);
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newCraftingResult(ServerPlayer holder) {
return new ContentCraftingResult(holder);
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newCursor(ServerPlayer holder) {
return new ContentCursor(holder);
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newDrop(ServerPlayer holder) {
return new ContentDrop(holder);
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newEquipment(
ServerPlayer holder,
EquipmentSlot equipmentSlot
) {
return new ContentEquipment(holder, equipmentSlot);
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newList(
ServerPlayer holder,
int index,
InventoryType.SlotType slotType,
Function<ServerPlayer, List<ItemStack>> listSupplier
) {
return new ContentList(holder, index, slotType) {
@Override
protected List<ItemStack> getItems(ServerPlayer holder) {
return listSupplier.apply(holder);
}
};
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newOffHand(ServerPlayer holder) {
return new ContentOffHand(holder);
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newViewOnly(ServerPlayer holder) {
return new ContentViewOnly(holder);
}
@Override
public Content<ServerPlayer, ItemStack, Container, Slot> newViewOnly(ServerPlayer holder, ItemStack itemStack) {
return new ContentViewOnly(holder) {
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotViewOnly(container, slot, x, y) {
@Override
public ItemStack getOrDefault() {
return itemStack;
}
};
}
};
}
@Override
public Slot wrapViewOnly(Slot wrapped) {
SlotViewOnly wrapper;
if (wrapped instanceof SlotPlaceholder placeholder) {
wrapper = new SlotViewOnly(wrapped.container, wrapped.getContainerSlot(), wrapped.x, wrapped.y) {
@Override
public ItemStack getOrDefault() {
return placeholder.getOrDefault();
}
};
} else {
wrapper = new SlotViewOnly(wrapped.container, wrapped.getContainerSlot(), wrapped.x, wrapped.y) {
@Override
public ItemStack getOrDefault() {
return wrapped.getItem();
}
};
}
wrapper.index = wrapped.index;
return wrapper;
}
@Override
public org.bukkit.inventory.ItemStack asBukkitMirror(ItemStack itemStack) {
return CraftItemStack.asBukkitMirror(itemStack);
}
}
@@ -0,0 +1,49 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@NullMarked
public class SlotEquipment extends SlotPlaceholder {
private final ItemStack placeholder;
private final EquipmentSlot equipmentSlot;
private @Nullable ServerPlayer viewer;
SlotEquipment(Container container, int index, int x, int y, ItemStack placeholder, EquipmentSlot equipmentSlot) {
super(container, index, x, y);
this.placeholder = placeholder;
this.equipmentSlot = equipmentSlot;
}
@Override
public ItemStack getOrDefault() {
ItemStack itemStack = getItem();
if (!itemStack.isEmpty()) {
return itemStack;
}
return placeholder;
}
public EquipmentSlot getEquipmentSlot() {
return equipmentSlot;
}
public void onlyEquipmentFor(ServerPlayer viewer) {
this.viewer = viewer;
}
@Override
public boolean mayPlace(ItemStack itemStack) {
if (viewer == null) {
return true;
}
return equipmentSlot == EquipmentSlot.OFFHAND || viewer.getEquipmentSlotForItem(itemStack) == equipmentSlot;
}
}
@@ -0,0 +1,22 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import net.minecraft.world.Container;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.NullMarked;
/**
* An implementation of a slot as used by a menu that may have fake placeholder items.
*
* <p>Used to prevent plugins (particularly sorting plugins) from adding placeholders to inventories.</p>
*/
@NullMarked
public abstract class SlotPlaceholder extends Slot {
SlotPlaceholder(Container container, int index, int x, int y) {
super(container, index, x, y);
}
public abstract ItemStack getOrDefault();
}
@@ -0,0 +1,131 @@
package com.lishid.openinv.internal.paper26_3.container.slot;
import com.lishid.openinv.internal.paper26_3.container.slot.placeholder.Placeholders;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.jspecify.annotations.NullMarked;
import java.util.Optional;
/**
* A view-only {@link Slot}. "Blank" by default, but can wrap another slot to display its content.
*/
@NullMarked
public class SlotViewOnly extends SlotPlaceholder {
public SlotViewOnly(Container container, int index, int x, int y) {
super(container, index, x, y);
}
@Override
public ItemStack getOrDefault() {
return Placeholders.notSlot;
}
@Override
public void onQuickCraft(ItemStack itemStack1, ItemStack itemStack2) {
}
@Override
public void onTake(Player player, ItemStack itemStack) {
}
@Override
public boolean mayPlace(ItemStack itemStack) {
return false;
}
@Override
public ItemStack getItem() {
return ItemStack.EMPTY;
}
@Override
public boolean hasItem() {
return false;
}
@Override
public void setByPlayer(ItemStack newStack) {
}
@Override
public void setByPlayer(ItemStack newStack, ItemStack oldStack) {
}
@Override
public void set(ItemStack itemStack) {
}
@Override
public void setChanged() {
}
@Override
public int getMaxStackSize() {
return 0;
}
@Override
public int getMaxStackSize(ItemStack itemStack) {
return 0;
}
@Override
public ItemStack remove(int amount) {
return ItemStack.EMPTY;
}
@Override
public boolean mayPickup(Player player) {
return false;
}
@Override
public boolean isActive() {
return false;
}
@Override
public Optional<ItemStack> tryRemove(int var0, int var1, Player player) {
return Optional.empty();
}
@Override
public ItemStack safeTake(int var0, int var1, Player player) {
return ItemStack.EMPTY;
}
@Override
public ItemStack safeClone(Player player) {
return ItemStack.EMPTY;
}
@Override
public ItemStack safeInsert(ItemStack itemStack) {
return itemStack;
}
@Override
public ItemStack safeInsert(ItemStack itemStack, int amount) {
return itemStack;
}
@Override
public boolean allowModification(Player player) {
return false;
}
@Override
public boolean isHighlightable() {
return false;
}
@Override
public boolean isFake() {
return true;
}
}
@@ -0,0 +1,197 @@
package com.lishid.openinv.internal.paper26_3.container.slot.placeholder;
import com.mojang.serialization.DataResult;
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.NbtOps;
import net.minecraft.nbt.TagParser;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.Item;
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.item.component.TooltipDisplay;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.entity.BannerPattern;
import net.minecraft.world.level.block.entity.BannerPatternLayers;
import net.minecraft.world.level.block.entity.BannerPatterns;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.craftbukkit.CraftRegistry;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.util.LinkedHashSet;
import java.util.List;
@NullMarked
public class PlaceholderLoader {
public void load(@Nullable ConfigurationSection section) throws Exception {
Placeholders.craftingOutput = parse(section, "crafting-output", defaultCraftingOutput());
Placeholders.cursor = parse(section, "cursor", defaultCursor());
Placeholders.drop = parse(section, "drop", defaultDrop());
Placeholders.emptyHelmet = parse(section, "empty-helmet", getEmptyArmor(Items.LEATHER_HELMET));
Placeholders.emptyChestplate = parse(section, "empty-chestplate", getEmptyArmor(Items.LEATHER_CHESTPLATE));
Placeholders.emptyLeggings = parse(section, "empty-leggings", getEmptyArmor(Items.LEATHER_LEGGINGS));
Placeholders.emptyBoots = parse(section, "empty-boots", getEmptyArmor(Items.LEATHER_BOOTS));
Placeholders.emptyOffHand = parse(section, "empty-off-hand", defaultShield());
Placeholders.notSlot = parse(section, "not-a-slot", defaultNotSlot());
Placeholders.blockedOffline = parse(section, "blocked.offline", defaultBlockedOffline());
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())
);
Placeholders.BLOCKED_GAME_TYPE.put(type, typeItem);
}
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)));
}
protected ItemStack parse(
@Nullable ConfigurationSection section,
String path,
ItemStack defaultStack
) throws Exception {
if (section == null) {
return defaultStack;
}
String itemText = section.getString(path);
if (itemText == null) {
return defaultStack;
}
CompoundTag compoundTag = TagParser.parseCompoundFully(itemText);
DataResult<ItemStack> parsed = ItemStack.CODEC.parse(CraftRegistry.getMinecraftRegistry().createSerializationContext(NbtOps.INSTANCE), compoundTag);
ItemStack itemStack;
try {
itemStack = parsed.getOrThrow();
} catch (Exception e) {
itemStack = null;
}
return itemStack == null ? defaultStack : itemStack;
}
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<>());
protected void addModelData(ItemStack itemStack) {
itemStack.set(DataComponents.CUSTOM_MODEL_DATA, DEFAULT_CUSTOM_MODEL_DATA);
}
protected void hideTooltip(ItemStack itemStack) {
itemStack.set(DataComponents.TOOLTIP_DISPLAY, HIDE_TOOLTIP);
}
protected ItemStack defaultCraftingOutput() {
// Crafting table: "Crafting"
ItemStack itemStack = new ItemStack(Items.CRAFTING_TABLE);
itemStack.set(DataComponents.ITEM_NAME, Component.translatable("container.crafting"));
addModelData(itemStack);
return itemStack;
}
protected ItemStack defaultCursor() {
// Cursor-like banner with no tooltip
ItemStack itemStack = new ItemStack(getDefaultCursorItem());
RegistryAccess minecraftRegistry = CraftRegistry.getMinecraftRegistry();
Registry<BannerPattern> bannerPatterns = minecraftRegistry.lookupOrThrow(Registries.BANNER_PATTERN);
BannerPattern halfDiagBottomRight = bannerPatterns.getOrThrow(BannerPatterns.DIAGONAL_RIGHT).value();
BannerPattern downRight = bannerPatterns.getOrThrow(BannerPatterns.STRIPE_DOWNRIGHT).value();
BannerPattern border = bannerPatterns.getOrThrow(BannerPatterns.BORDER).value();
itemStack.set(DataComponents.BANNER_PATTERNS,
new BannerPatternLayers(List.of(
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfDiagBottomRight), DyeColor.GRAY),
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(downRight), DyeColor.WHITE),
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(border), DyeColor.GRAY)
))
);
addModelData(itemStack);
hideTooltip(itemStack);
return itemStack;
}
protected 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"));
addModelData(itemStack);
return itemStack;
}
protected ItemStack getEmptyArmor(ItemLike item) {
// Inventory-background-grey-ish leather armor with no tooltip
ItemStack itemStack = new ItemStack(item);
DyedItemColor color = new DyedItemColor(0xC8C8C8);
itemStack.set(DataComponents.DYED_COLOR, color);
hideTooltip(itemStack);
addModelData(itemStack);
return itemStack;
}
protected 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.lookupOrThrow(Registries.BANNER_PATTERN);
BannerPattern halfLeft = bannerPatterns.getOrThrow(BannerPatterns.HALF_VERTICAL).value();
BannerPattern topLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_LEFT).value();
BannerPattern topRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_TOP_RIGHT).value();
BannerPattern bottomLeft = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_LEFT).value();
BannerPattern bottomRight = bannerPatterns.getOrThrow(BannerPatterns.SQUARE_BOTTOM_RIGHT).value();
itemStack.set(DataComponents.BANNER_PATTERNS,
new BannerPatternLayers(List.of(
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(halfLeft), DyeColor.BLACK),
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topLeft), DyeColor.MAGENTA),
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomLeft), DyeColor.MAGENTA),
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(topRight), DyeColor.BLACK),
new BannerPatternLayers.Layer(bannerPatterns.wrapAsHolder(bottomRight), DyeColor.BLACK)
))
);
hideTooltip(itemStack);
addModelData(itemStack);
return itemStack;
}
protected ItemStack defaultNotSlot() {
// White pane with no tooltip
ItemStack itemStack = new ItemStack(getDefaultNotSlotItem());
hideTooltip(itemStack);
addModelData(itemStack);
return itemStack;
}
protected Item getDefaultCursorItem() {
return Items.BANNER.white();
}
protected Item getDefaultNotSlotItem() {
return Items.STAINED_GLASS_PANE.white();
}
protected ItemStack defaultBlockedOffline() {
// Barrier: "Not available - Offline"
ItemStack itemStack = new ItemStack(Items.BARRIER);
itemStack.set(DataComponents.ITEM_NAME,
Component.translatable("options.narrator.notavailable")
.append(Component.literal(" - "))
.append(Component.translatable("gui.socialInteractions.status_offline"))
);
return itemStack;
}
}
@@ -0,0 +1,42 @@
package com.lishid.openinv.internal.paper26_3.container.slot.placeholder;
import com.lishid.openinv.internal.paper26_3.player.OpenPlayer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameType;
import org.jspecify.annotations.NullMarked;
import java.util.EnumMap;
@NullMarked
public final class Placeholders {
// N.B. Slot#getNoItemIcon is still clientside, unfortunately.
// Worth checking every few updates just in case - it would be nice to
// get rid of some of the placeholders.
static final EnumMap<GameType, ItemStack> BLOCKED_GAME_TYPE = new EnumMap<>(GameType.class);
public static ItemStack craftingOutput = ItemStack.EMPTY;
public static ItemStack cursor = ItemStack.EMPTY;
public static ItemStack drop = ItemStack.EMPTY;
public static ItemStack emptyHelmet = ItemStack.EMPTY;
public static ItemStack emptyChestplate = ItemStack.EMPTY;
public static ItemStack emptyLeggings = ItemStack.EMPTY;
public static ItemStack emptyBoots = ItemStack.EMPTY;
public static ItemStack emptyOffHand = ItemStack.EMPTY;
public static ItemStack notSlot = ItemStack.EMPTY;
public static ItemStack blockedOffline = ItemStack.EMPTY;
public static ItemStack survivalOnly(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.");
}
}
@@ -0,0 +1,224 @@
package com.lishid.openinv.internal.paper26_3.player;
import com.lishid.openinv.event.OpenEvents;
import com.mojang.logging.LogUtils;
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.server.network.ServerGamePacketListenerImpl;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.level.storage.PlayerDataStorage;
import net.minecraft.world.level.storage.TagValueOutput;
import net.minecraft.world.level.storage.ValueOutput;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
@NullMarked
public class OpenPlayer extends CraftPlayer {
/**
* List of tags to always reset when saving. These are items that do not get written
* if unset or empty, resulting in older values not being clobbered appropriately.
*
* @see net.minecraft.world.entity.Entity#saveWithoutId(ValueOutput, boolean, boolean, boolean)
* @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(ValueOutput)
* @see net.minecraft.world.entity.player.Player#addAdditionalSaveData(ValueOutput)
* @see net.minecraft.world.entity.LivingEntity#addAdditionalSaveData(ValueOutput)
*/
@Unmodifiable
protected static final Set<String> RESET_TAGS = Set.of(
// Entity#saveWithoutId(CompoundTag)
"invulnerable_time",
"CustomName",
"CustomNameVisible",
"Silent",
"NoGravity",
"Glowing",
"TicksFrozen",
"HasVisualFire",
"Tags",
"data",
"Passengers",
// ServerPlayer#addAdditionalSaveData(CompoundTag)
// Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle
"entered_nether_pos",
"last_explosion_impact_pos",
"respawn",
"raid_omen_position",
"ender_pearls",
"ShoulderEntityLeft",
"ShoulderEntityRight",
// Player#addAdditionalSaveData(CompoundTag)
"LastDeathLocation",
// LivingEntity#addAdditionalSaveData(CompoundTag)
"current_explosion_impact_pos",
"active_effects",
"sleeping_pos",
"Brain", // No longer needed as of 26.1
"last_hurt_by_player",
"last_hurt_by_player_memory_time",
"last_hurt_by_mob",
"ticks_since_last_hurt_by_mob",
"equipment",
"locator_bar_icon"
);
private final PlayerManager manager;
protected OpenPlayer(CraftServer server, ServerPlayer entity, PlayerManager manager) {
super(server, entity);
this.manager = manager;
}
@Override
public void loadData() {
manager.loadData(server.getServer(), getHandle());
}
@Override
public void saveData() {
if (OpenEvents.saveCancelled(this)) {
return;
}
ServerPlayer player = getHandle();
Logger logger = LogUtils.getLogger();
// See net.minecraft.world.level.storage.PlayerDataStorage#save(EntityHuman)
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), logger)) {
PlayerDataStorage worldNbtStorage = server.getServer().getPlayerList().playerIo;
CompoundTag oldData;
oldData = isOnline() ? null : worldNbtStorage.load(player.nameAndId()).orElse(null);
CompoundTag playerData = getWritableTag(oldData);
ValueOutput valueOutput = TagValueOutput.createWrappingWithContext(scopedCollector, player.registryAccess(), playerData);
player.saveWithoutId(valueOutput);
saveSafe(player, oldData, playerData, worldNbtStorage);
} catch (Exception e) {
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
}
}
protected void saveSafe(
ServerPlayer player,
@Nullable CompoundTag oldData,
CompoundTag playerData,
PlayerDataStorage worldNbtStorage
) throws IOException {
// Revert certain special data values when offline.
revertSpecialValues(playerData, oldData);
Path playerDataDir = worldNbtStorage.getPlayerDir().toPath();
Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
NbtIo.writeCompressed(playerData, tempFile);
Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
safeReplaceFile(dataFile, tempFile, backupFile);
}
protected void safeReplaceFile(
Path dataFile,
Path tempFile,
Path backupFile
) {
net.minecraft.util.Util.safeReplaceFile(dataFile, tempFile, backupFile);
}
@Contract("null -> new")
protected 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.keySet().removeIf(
key -> RESET_TAGS.contains(key)
|| key.startsWith("Bukkit")
|| (key.startsWith("Paper") && key.length() > 5)
);
return oldData;
}
protected void revertSpecialValues(CompoundTag newData, @Nullable CompoundTag oldData) {
if (oldData == null) {
return;
}
// 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(
CompoundTag source,
CompoundTag target,
String container,
String key,
@SuppressWarnings("SameParameterValue") 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,
String key,
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(
CompoundTag container,
String key,
@Nullable T data
) {
if (data == null) {
remove(container, key);
} else {
container.put(key, data);
}
}
protected void remove(CompoundTag tag, String key) {
tag.remove(key);
}
public static boolean isConnected(@Nullable ServerGamePacketListenerImpl connection) {
return connection != null && !connection.isDisconnected();
}
}
@@ -0,0 +1,340 @@
package com.lishid.openinv.internal.paper26_3.player;
import com.lishid.openinv.internal.ISpecialInventory;
import com.github.jikoo.openinv.internal.container.slot.InventoryFactory;
import com.lishid.openinv.internal.paper26_3.container.BaseOpenInventory;
import com.lishid.openinv.internal.paper26_3.container.OpenEnderChest;
import com.lishid.openinv.internal.paper26_3.container.menu.OpenChestMenu;
import com.github.jikoo.openinv.util.JulLoggerAdapter;
import com.mojang.authlib.GameProfile;
import com.mojang.logging.LogUtils;
import io.papermc.paper.adventure.PaperAdventure;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ParticleStatus;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.Container;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.storage.LevelData;
import net.minecraft.world.level.storage.TagValueInput;
import net.minecraft.world.level.storage.TagValueOutput;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.phys.Vec3;
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.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.Optional;
import java.util.UUID;
import java.util.logging.Logger;
@NullMarked
public class PlayerManager implements com.github.jikoo.openinv.internal.PlayerManager {
protected final Logger logger;
protected final InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory;
protected @Nullable Field bukkitEntity;
public PlayerManager(
Logger logger,
InventoryFactory<ServerPlayer, ItemStack, Container, Slot, EquipmentSlot> factory
) {
this.logger = logger;
this.factory = factory;
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 ServerPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer craftPlayer) {
return craftPlayer.getHandle();
}
Server server = player.getServer();
ServerPlayer nmsPlayer = null;
if (server instanceof CraftServer craftServer) {
nmsPlayer = craftServer.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(final OfflinePlayer offline) {
if (!(Bukkit.getServer() instanceof CraftServer craftServer)) {
return null;
}
MinecraftServer server = craftServer.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.
removeAdvancementListeners(entity);
// Try to load the player's data.
if (loadData(server, entity)) {
// If data is loaded successfully, return the Bukkit entity.
return entity.getBukkitEntity();
}
return null;
}
protected ServerPlayer createNewPlayer(
MinecraftServer server,
ServerLevel worldServer,
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(server, 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;
}
protected void removeAdvancementListeners(ServerPlayer entity) {
entity.getAdvancements().clearTriggers();
}
protected boolean loadData(MinecraftServer server, ServerPlayer player) {
// See CraftPlayer#loadData
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), new JulLoggerAdapter(logger))) {
CompoundTag loadedData = server.getPlayerList().playerIo.load(player.nameAndId()).orElse(null);
if (loadedData == null) {
// Exceptions with loading are logged.
return false;
}
ValueInput valueInput = TagValueInput.create(scopedCollector, player.registryAccess(), loadedData);
// Read basic data into the player.
player.load(valueInput);
// World is not loaded by ServerPlayer#load(CompoundTag) on Paper.
parseWorld(server, player, valueInput);
}
return true;
}
protected void parseWorld(
MinecraftServer server,
ServerPlayer player,
ValueInput 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 {
bukkitWorld = loadedData.getString("world").map(Bukkit::getWorld).orElse(null);
}
if (bukkitWorld == null) {
spawnInDefaultWorld(server, player);
return;
}
player.setServerLevel(((CraftWorld) bukkitWorld).getHandle());
}
protected void spawnInDefaultWorld(MinecraftServer server, ServerPlayer player) {
ServerLevel level = server.getLevel(Level.OVERWORLD);
if (level != null) {
// Adjust player to default spawn (in keeping with Paper handling) when world not found.
LevelData.RespawnData respawnData = getRespawnData(level);
player.snapTo(getAdjustedSpawnLocation(player, level, respawnData), respawnData.yaw(), 0.0F);
setServerLevel(player, level);
} else {
logger.warning("Tried to load player with invalid world when no fallback was available!");
}
}
protected LevelData.RespawnData getRespawnData(ServerLevel level) {
return level.getRespawnData();
}
protected Vec3 getAdjustedSpawnLocation(
ServerPlayer player,
ServerLevel level,
LevelData.RespawnData respawnData
) {
return Vec3.atBottomCenterOf(player.adjustSpawnLocation(level, respawnData.pos()));
}
protected void setServerLevel(ServerPlayer player, ServerLevel level) {
player.setServerLevel(level);
}
protected void injectPlayer(MinecraftServer server, ServerPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new OpenPlayer(server.server, player, this));
}
@Override
public Player inject(Player player) {
try {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
return openPlayer;
}
org.slf4j.Logger logger = LogUtils.getLogger();
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(nmsPlayer.problemPath(), logger)) {
CompoundTag extraData = new CompoundTag();
// Copy extra data out of existing player.
ValueOutput output = TagValueOutput.createWrappingWithContext(scopedCollector, nmsPlayer.registryAccess(), extraData);
nmsPlayer.getBukkitEntity().setExtraData(output);
MinecraftServer server = nmsPlayer.level().getServer();
injectPlayer(server, nmsPlayer);
CraftPlayer newPlayer = nmsPlayer.getBukkitEntity();
// Set extra data in new player.
ValueInput input = TagValueInput.create(scopedCollector, nmsPlayer.registryAccess(), extraData);
newPlayer.readExtraData(input);
return newPlayer;
}
} 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(
Player bukkitPlayer,
ISpecialInventory inventory,
boolean viewOnly
) {
ServerPlayer player = getHandle(bukkitPlayer);
if (!OpenPlayer.isConnected(player.connection)) {
return null;
}
// See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider)
OpenChestMenu<?> menu;
Component title;
if (inventory instanceof BaseOpenInventory playerInv) {
menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly);
title = playerInv.getTitle(player, menu);
} else if (inventory instanceof OpenEnderChest enderChest) {
menu = enderChest.createMenu(factory, player, player.nextContainerCounter(), viewOnly);
title = enderChest.getTitle(menu);
} 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);
AbstractContainerMenu opened = pair.getSecond();
// Menu is null if event is cancelled.
if (opened == null) {
return null;
}
var newTitle = pair.getFirst();
if (newTitle != null) {
title = PaperAdventure.asVanilla(newTitle);
}
player.containerMenu = opened;
player.connection.send(new ClientboundOpenScreenPacket(opened.containerId, opened.getType(), title));
player.initMenu(opened);
return opened.getBukkitView();
}
}
Loaded 100 of 339 files, more files were not shown because too many files have changed in this diff. Show more