Compare commits

...
23 Commits
Author SHA1 Message Date
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
38 changed files with 725 additions and 412 deletions

No files matched your search

+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
# Note: this is directly from GitHub's example for using data from a triggering workflow:
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow
- name: 'Download artifact'
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
+3 -3
View File
@@ -15,9 +15,9 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: actions/setup-java@v4
- uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '21'
@@ -33,7 +33,7 @@ jobs:
restore-keys: |
${{ runner.os }}-buildtools-
- uses: gradle/actions/setup-gradle@v4
- uses: gradle/actions/setup-gradle@v5
- name: Build with Gradle
run: ./gradlew clean build
+3 -3
View File
@@ -13,21 +13,21 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download build
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: dist
path: dist
- name: Create Release
id: create-release
uses: softprops/action-gh-release@v2.3.2
uses: softprops/action-gh-release@v2.3.3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
name: OpenInv ${{ env.VERSIONED_NAME }}
body: |-
## Supported server versions
**Paper:** TODO VERSION, 1.21.4, 1.21.3, 1.21.1
**Paper:** TODO VERSION, 1.21.8, 1.21.7, 1.21.6, 1.21.5, 1.21.4, 1.21.3, 1.21.1
**Spigot:** TODO VERSION
TODO HELLO HUMAN, PRESS THE GENERATE CHANGELOG BUTTON PLEASE.
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build resource pack
id: upload-resource-pack
+1 -1
View File
@@ -1,6 +1,6 @@
# Project meta
group=com.lishid.openinv
version=5.1.12
version=5.1.15
description=A Bukkit plugin for opening normally-inaccessible inventories.
# Gradle configuration
+3 -3
View File
@@ -2,11 +2,11 @@
spigotapi = "1.21.5-R0.1-SNAPSHOT"
specialsource = "1.11.5"
planarwrappers = "3.3.0"
annotations = "26.0.2"
annotations = "26.0.2-1"
paperweight = "2.0.0-beta.17"
shadow = "8.3.8"
shadow = "9.2.2"
folia-scheduler-wrapper = "v0.0.3"
errorprone-core = "2.39.0"
errorprone-core = "2.42.0"
errorprone-gradle = "4.2.0"
slf4j = "2.0.17"
+1 -1
View File
@@ -33,7 +33,7 @@ dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
paperweight.paperDevBundle("1.21.7-R0.1-SNAPSHOT")
paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT")
}
val spigot = tasks.register<ShadowJar>("spigotRelocations") {
@@ -0,0 +1,335 @@
package com.lishid.openinv.internal.common.container;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.internal.InternalOwned;
import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventory;
import com.lishid.openinv.internal.common.container.menu.OpenInventoryMenu;
import com.lishid.openinv.internal.common.container.slot.Content;
import com.lishid.openinv.internal.common.container.slot.ContentCrafting;
import com.lishid.openinv.internal.common.container.slot.ContentCraftingResult;
import com.lishid.openinv.internal.common.container.slot.ContentCursor;
import com.lishid.openinv.internal.common.container.slot.ContentDrop;
import com.lishid.openinv.internal.common.container.slot.ContentEquipment;
import com.lishid.openinv.internal.common.container.slot.ContentList;
import com.lishid.openinv.internal.common.container.slot.ContentOffHand;
import com.lishid.openinv.internal.common.container.slot.ContentViewOnly;
import com.lishid.openinv.internal.common.container.slot.SlotViewOnly;
import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.common.player.PlayerManager;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.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.AbstractContainerMenu;
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.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
public abstract class BaseOpenInventory implements Container, InternalOwned<ServerPlayer>, ISpecialPlayerInventory {
protected final List<Content> slots;
private final int size;
protected ServerPlayer owner;
private int maxStackSize = 99;
protected CraftInventory bukkitEntity;
public List<HumanEntity> transaction = new ArrayList<>();
public BaseOpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) {
owner = PlayerManager.getHandle(bukkitPlayer);
// Get total size, rounding up to nearest 9 for client compatibility.
int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1;
size = ((int) Math.ceil(rawSize / 9.0)) * 9;
slots = NonNullList.withSize(size, new ContentViewOnly(owner));
setupSlots();
}
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, new ContentDrop(owner));
// Cursor slot: Above drop.
slots.set(44, new ContentCursor(owner));
// Crafting is displayed in the bottom right corner.
// As we're using the pretty view, this is a 3x2.
addCrafting(41, true);
return;
}
// Otherwise we'll just add elements linearly.
nextIndex = addArmor(nextIndex);
nextIndex = addOffHand(nextIndex);
nextIndex = addCrafting(nextIndex, false);
slots.set(nextIndex, new ContentCursor(owner));
// Drop slot last.
slots.set(slots.size() - 1, new ContentDrop(owner));
}
private int addMainInventory() {
int listSize = owner.getInventory().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,
new ContentList(owner, invIndex, type) {
@Override
public void setHolder(@NotNull ServerPlayer holder) {
items = 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, new ContentEquipment(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, new ContentOffHand(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, new ContentCrafting(owner, localIndex));
}
if (pretty) {
slots.set(startIndex + 2, new ContentViewOnly(owner) {
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotViewOnly(container, slot, x, y) {
@Override
public ItemStack getOrDefault() {
return Placeholders.craftingOutput;
}
};
}
}
);
slots.set(startIndex + 11, getCraftingResult(owner));
}
return startIndex + listSize;
}
protected Content getCraftingResult(@NotNull ServerPlayer serverPlayer) {
return new ContentCraftingResult(serverPlayer);
}
public Slot getMenuSlot(int index, int x, int y) {
return slots.get(index).asSlot(this, index, x, y);
}
public InventoryType.SlotType getSlotType(int index) {
return slots.get(index).getSlotType();
}
public abstract Component getTitle(ServerPlayer player);
@Override
public ServerPlayer getOwnerHandle() {
return owner;
}
@Override
public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() {
if (bukkitEntity == null) {
bukkitEntity = new OpenPlayerInventory(this);
}
return bukkitEntity;
}
@Override
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
ServerPlayer newOwner = PlayerManager.getHandle(player);
// Only transfer regular inventory - crafting and cursor slots are transient.
newOwner.getInventory().replaceWith(owner.getInventory());
owner = newOwner;
// Update slots to point to new inventory.
slots.forEach(slot -> slot.setHolder(newOwner));
}
@Override
public boolean isInUse() {
return !transaction.isEmpty();
}
@Override
public @NotNull org.bukkit.entity.Player getPlayer() {
return getOwner();
}
@Override
public int getContainerSize() {
return size;
}
@Override
public boolean isEmpty() {
return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty);
}
@Override
public @NotNull ItemStack getItem(int index) {
return slots.get(index).get();
}
@Override
public @NotNull ItemStack removeItem(int index, int amount) {
return slots.get(index).removePartial(amount);
}
@Override
public @NotNull ItemStack removeItemNoUpdate(int index) {
return slots.get(index).remove();
}
@Override
public void setItem(int index, @NotNull 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(@NotNull Player player) {
return true;
}
@Override
public @NotNull List<ItemStack> getContents() {
NonNullList<ItemStack> contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY);
for (int i = 0; i < getContainerSize(); ++i) {
contents.set(i, getItem(i));
}
return contents;
}
@Override
public void onOpen(@NotNull CraftHumanEntity viewer) {
transaction.add(viewer);
}
@Override
public void onClose(@NotNull CraftHumanEntity viewer) {
transaction.remove(viewer);
}
@Override
public @NotNull List<HumanEntity> getViewers() {
return transaction;
}
@Override
public @NotNull org.bukkit.entity.Player getOwner() {
return owner.getBukkitEntity();
}
@Override
public Location getLocation() {
return owner.getBukkitEntity().getLocation();
}
@Override
public void clearContent() {
owner.getInventory().clearContent();
owner.inventoryMenu.getCraftSlots().clearContent();
owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots());
owner.containerMenu.setCarried(ItemStack.EMPTY);
}
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
if (player instanceof ServerPlayer serverPlayer) {
return new OpenInventoryMenu(this, serverPlayer, i, viewOnly);
}
return null;
}
}
@@ -1,207 +1,22 @@
package com.lishid.openinv.internal.common.container;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.internal.InternalOwned;
import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventory;
import com.lishid.openinv.internal.common.container.menu.OpenInventoryMenu;
import com.lishid.openinv.internal.common.container.slot.Content;
import com.lishid.openinv.internal.common.container.slot.ContentCrafting;
import com.lishid.openinv.internal.common.container.slot.ContentCraftingResult;
import com.lishid.openinv.internal.common.container.slot.ContentCursor;
import com.lishid.openinv.internal.common.container.slot.ContentDrop;
import com.lishid.openinv.internal.common.container.slot.ContentEquipment;
import com.lishid.openinv.internal.common.container.slot.ContentList;
import com.lishid.openinv.internal.common.container.slot.ContentOffHand;
import com.lishid.openinv.internal.common.container.slot.ContentViewOnly;
import com.lishid.openinv.internal.common.container.slot.SlotViewOnly;
import com.lishid.openinv.internal.common.container.slot.placeholder.Placeholders;
import com.lishid.openinv.internal.common.player.PlayerManager;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.ChatFormatting;
import net.minecraft.core.NonNullList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FontDescription;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.Location;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
public class OpenInventory extends BaseOpenInventory {
public class OpenInventory implements Container, InternalOwned<ServerPlayer>, ISpecialPlayerInventory {
protected final List<Content> slots;
private final int size;
protected ServerPlayer owner;
private int maxStackSize = 99;
protected CraftInventory bukkitEntity;
public List<HumanEntity> transaction = new ArrayList<>();
public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) {
owner = PlayerManager.getHandle(bukkitPlayer);
// Get total size, rounding up to nearest 9 for client compatibility.
int rawSize = owner.getInventory().getContainerSize() + owner.inventoryMenu.getCraftSlots().getContainerSize() + 1;
size = ((int) Math.ceil(rawSize / 9.0)) * 9;
slots = NonNullList.withSize(size, new ContentViewOnly(owner));
setupSlots();
}
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, new ContentDrop(owner));
// Cursor slot: Above drop.
slots.set(44, new ContentCursor(owner));
// Crafting is displayed in the bottom right corner.
// As we're using the pretty view, this is a 3x2.
addCrafting(41, true);
return;
}
// Otherwise we'll just add elements linearly.
nextIndex = addArmor(nextIndex);
nextIndex = addOffHand(nextIndex);
nextIndex = addCrafting(nextIndex, false);
slots.set(nextIndex, new ContentCursor(owner));
// Drop slot last.
slots.set(slots.size() - 1, new ContentDrop(owner));
}
private int addMainInventory() {
int listSize = owner.getInventory().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,
new ContentList(owner, invIndex, type) {
@Override
public void setHolder(@NotNull ServerPlayer holder) {
items = 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, new ContentEquipment(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, new ContentOffHand(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, new ContentCrafting(owner, localIndex));
}
if (pretty) {
slots.set(startIndex + 2, new ContentViewOnly(owner) {
@Override
public Slot asSlot(Container container, int slot, int x, int y) {
return new SlotViewOnly(container, slot, x, y) {
@Override
public ItemStack getOrDefault() {
return Placeholders.craftingOutput;
}
};
}
}
);
slots.set(startIndex + 11, getCraftingResult(owner));
}
return startIndex + listSize;
}
protected Content getCraftingResult(@NotNull ServerPlayer serverPlayer) {
return new ContentCraftingResult(serverPlayer);
}
public Slot getMenuSlot(int index, int x, int y) {
return slots.get(index).asSlot(this, index, x, y);
}
public InventoryType.SlotType getSlotType(int index) {
return slots.get(index).getSlotType();
public OpenInventory(@NotNull Player bukkitPlayer) {
super(bukkitPlayer);
}
@Override
public @NotNull Component getTitle(@Nullable ServerPlayer viewer) {
MutableComponent component = Component.empty();
// Prefix for use with custom bitmap image fonts.
@@ -209,13 +24,13 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
component.append(
Component.translatableWithFallback("openinv.container.inventory.self", "")
.withStyle(style -> style
.withFont(ResourceLocation.parse("openinv:font/inventory"))
.withFont(new FontDescription.Resource(ResourceLocation.parse("openinv:font/inventory")))
.withColor(ChatFormatting.WHITE)));
} else {
component.append(
Component.translatableWithFallback("openinv.container.inventory.other", "")
.withStyle(style -> style
.withFont(ResourceLocation.parse("openinv:font/inventory"))
.withFont(new FontDescription.Resource(ResourceLocation.parse("openinv:font/inventory")))
.withColor(ChatFormatting.WHITE)));
}
// Normal title: "Inventory - OwnerName"
@@ -225,135 +40,4 @@ public class OpenInventory implements Container, InternalOwned<ServerPlayer>, IS
return component;
}
@Override
public ServerPlayer getOwnerHandle() {
return owner;
}
@Override
public @NotNull org.bukkit.inventory.Inventory getBukkitInventory() {
if (bukkitEntity == null) {
bukkitEntity = new OpenPlayerInventory(this);
}
return bukkitEntity;
}
@Override
public void setPlayerOnline(@NotNull org.bukkit.entity.Player player) {
ServerPlayer newOwner = PlayerManager.getHandle(player);
// Only transfer regular inventory - crafting and cursor slots are transient.
newOwner.getInventory().replaceWith(owner.getInventory());
owner = newOwner;
// Update slots to point to new inventory.
slots.forEach(slot -> slot.setHolder(newOwner));
}
@Override
public boolean isInUse() {
return !transaction.isEmpty();
}
@Override
public @NotNull org.bukkit.entity.Player getPlayer() {
return getOwner();
}
@Override
public int getContainerSize() {
return size;
}
@Override
public boolean isEmpty() {
return slots.stream().map(Content::get).allMatch(ItemStack::isEmpty);
}
@Override
public @NotNull ItemStack getItem(int index) {
return slots.get(index).get();
}
@Override
public @NotNull ItemStack removeItem(int index, int amount) {
return slots.get(index).removePartial(amount);
}
@Override
public @NotNull ItemStack removeItemNoUpdate(int index) {
return slots.get(index).remove();
}
@Override
public void setItem(int index, @NotNull 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(@NotNull Player player) {
return true;
}
@Override
public @NotNull List<ItemStack> getContents() {
NonNullList<ItemStack> contents = NonNullList.withSize(getContainerSize(), ItemStack.EMPTY);
for (int i = 0; i < getContainerSize(); ++i) {
contents.set(i, getItem(i));
}
return contents;
}
@Override
public void onOpen(@NotNull CraftHumanEntity viewer) {
transaction.add(viewer);
}
@Override
public void onClose(@NotNull CraftHumanEntity viewer) {
transaction.remove(viewer);
}
@Override
public @NotNull List<HumanEntity> getViewers() {
return transaction;
}
@Override
public @NotNull org.bukkit.entity.Player getOwner() {
return owner.getBukkitEntity();
}
@Override
public Location getLocation() {
return owner.getBukkitEntity().getLocation();
}
@Override
public void clearContent() {
owner.getInventory().clearContent();
owner.inventoryMenu.getCraftSlots().clearContent();
owner.inventoryMenu.slotsChanged(owner.inventoryMenu.getCraftSlots());
owner.containerMenu.setCarried(ItemStack.EMPTY);
}
public @Nullable AbstractContainerMenu createMenu(Player player, int i, boolean viewOnly) {
if (player instanceof ServerPlayer serverPlayer) {
return new OpenInventoryMenu(this, serverPlayer, i, viewOnly);
}
return null;
}
}
@@ -1,7 +1,7 @@
package com.lishid.openinv.internal.common.container.bukkit;
import com.google.common.base.Preconditions;
import com.lishid.openinv.internal.common.container.OpenInventory;
import com.lishid.openinv.internal.common.container.BaseOpenInventory;
import net.minecraft.core.NonNullList;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Inventory;
@@ -18,13 +18,13 @@ import java.util.List;
public class OpenPlayerInventory extends CraftInventory implements PlayerInventory {
public OpenPlayerInventory(@NotNull OpenInventory inventory) {
public OpenPlayerInventory(@NotNull BaseOpenInventory inventory) {
super(inventory);
}
@Override
public @NotNull OpenInventory getInventory() {
return (OpenInventory) super.getInventory();
public @NotNull BaseOpenInventory getInventory() {
return (BaseOpenInventory) super.getInventory();
}
@Override
@@ -1,6 +1,6 @@
package com.lishid.openinv.internal.common.container.bukkit;
import com.lishid.openinv.internal.common.container.OpenInventory;
import com.lishid.openinv.internal.common.container.BaseOpenInventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
@@ -8,7 +8,7 @@ public class OpenPlayerInventorySelf extends OpenPlayerInventory {
private final int offset;
public OpenPlayerInventorySelf(@NotNull OpenInventory inventory, int offset) {
public OpenPlayerInventorySelf(@NotNull BaseOpenInventory inventory, int offset) {
super(inventory);
this.offset = offset;
}
@@ -364,6 +364,18 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
}
}
@Override
public void forceSlot(@NotNull Container container, int slot) {
int slotsIndex = this.findSlot(container, slot).orElse(-1);
if (slotsIndex != -1) {
ItemStack item = this.slots.get(slotsIndex).getItem();
this.remoteSlots.get(slotsIndex).force(item);
if (this.synchronizer != null) {
this.synchronizer.sendSlotChange(this, slotsIndex, item.copy());
}
}
}
@Override
public void broadcastCarriedItem() {
ItemStack carried = this.getCarried();
@@ -424,7 +436,8 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
}
}
private void triggerSlotListeners(int index, ItemStack itemStack, Supplier<ItemStack> supplier) {
@Override
public void triggerSlotListeners(int index, @NotNull ItemStack itemStack, @NotNull Supplier<ItemStack> supplier) {
ItemStack itemStack1 = this.lastSlots.get(index);
if (!ItemStack.matches(itemStack1, itemStack)) {
ItemStack itemStack2 = supplier.get();
@@ -436,7 +449,8 @@ public abstract class OpenChestMenu<T extends Container & ISpecialInventory & In
}
}
private void synchronizeSlotToRemote(int i, ItemStack itemStack, Supplier<ItemStack> supplier) {
@Override
public void synchronizeSlotToRemote(int i, @NotNull ItemStack itemStack, @NotNull Supplier<ItemStack> supplier) {
if (!this.suppressRemoteUpdates) {
RemoteSlot slot = this.remoteSlots.get(i);
if (!slot.matches(itemStack)) {
@@ -1,7 +1,7 @@
package com.lishid.openinv.internal.common.container.menu;
import com.google.common.base.Preconditions;
import com.lishid.openinv.internal.common.container.OpenInventory;
import com.lishid.openinv.internal.common.container.BaseOpenInventory;
import com.lishid.openinv.internal.common.container.bukkit.OpenDummyPlayerInventory;
import com.lishid.openinv.internal.common.container.bukkit.OpenPlayerInventorySelf;
import com.lishid.openinv.internal.common.container.slot.ContentDrop;
@@ -23,15 +23,15 @@ import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class OpenInventoryMenu extends OpenChestMenu<OpenInventory> {
public class OpenInventoryMenu extends OpenChestMenu<BaseOpenInventory> {
private int offset;
public OpenInventoryMenu(OpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) {
public OpenInventoryMenu(BaseOpenInventory inventory, ServerPlayer viewer, int i, boolean viewOnly) {
super(getMenuType(inventory, viewer), i, inventory, viewer, viewOnly);
}
private static MenuType<ChestMenu> getMenuType(OpenInventory inventory, ServerPlayer viewer) {
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)) {
@@ -99,7 +99,7 @@ public class OpenInventoryMenu extends OpenChestMenu<OpenInventory> {
}
@Override
protected @NotNull CraftInventoryView<OpenChestMenu<OpenInventory>, Inventory> createBukkitEntity() {
protected @NotNull CraftInventoryView<OpenChestMenu<BaseOpenInventory>, Inventory> createBukkitEntity() {
org.bukkit.inventory.Inventory bukkitInventory;
if (viewOnly) {
bukkitInventory = new OpenDummyPlayerInventory(container);
@@ -18,7 +18,8 @@ import java.util.Set;
public abstract class BaseOpenPlayer extends CraftPlayer {
/**
* List of tags to always reset when saving.
* 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)
* @see net.minecraft.server.level.ServerPlayer#addAdditionalSaveData(ValueOutput)
@@ -36,6 +37,7 @@ public abstract class BaseOpenPlayer extends CraftPlayer {
"TicksFrozen",
"HasVisualFire",
"Tags",
"data",
"Passengers",
// ServerPlayer#addAdditionalSaveData(CompoundTag)
// Intentional omissions to prevent mount loss: Attach, Entity, and RootVehicle
@@ -51,9 +53,9 @@ public abstract class BaseOpenPlayer extends CraftPlayer {
"SpawnDimension",
"raid_omen_position",
"ender_pearls",
// Player#addAdditionalSaveData(CompoundTag)
"ShoulderEntityLeft",
"ShoulderEntityRight",
// Player#addAdditionalSaveData(CompoundTag)
"LastDeathLocation",
"current_explosion_impact_pos",
// LivingEntity#addAdditionalSaveData(CompoundTag)
@@ -40,7 +40,7 @@ public class OpenPlayer extends BaseOpenPlayer {
CompoundTag oldData = isOnline()
? null
: worldNBTStorage.load(player.getName().getString(), player.getStringUUID(), scopedCollector).orElse(null);
: worldNBTStorage.load(player.nameAndId()).orElse(null);
CompoundTag playerData = getWritableTag(oldData);
ValueOutput valueOutput = TagValueOutput.createWrappingWithContext(scopedCollector, player.registryAccess(), playerData);
@@ -1,11 +1,13 @@
package com.lishid.openinv.internal.common.player;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.common.container.BaseOpenInventory;
import com.lishid.openinv.internal.common.container.OpenEnderChest;
import com.lishid.openinv.internal.common.container.OpenInventory;
import com.lishid.openinv.util.JulLoggerAdapter;
import com.mojang.authlib.GameProfile;
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;
@@ -18,6 +20,8 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.storage.LevelData;
import net.minecraft.world.level.storage.TagValueInput;
import net.minecraft.world.level.storage.ValueInput;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
@@ -143,26 +147,30 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
// See CraftPlayer#loadData
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), new JulLoggerAdapter(logger))) {
ValueInput loadedData = server.getPlayerList().playerIo.load(player, scopedCollector).orElse(null);
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(loadedData);
// Game type settings are loaded separately.
player.loadGameTypes(loadedData);
player.load(valueInput);
// World is not loaded by ServerPlayer#load(CompoundTag) on Paper.
parseWorld(server, player, loadedData);
parseWorld(server, player, valueInput);
}
return true;
}
private void parseWorld(@NotNull MinecraftServer server, @NotNull ServerPlayer player, @NotNull ValueInput loadedData) {
protected void parseWorld(
@NotNull MinecraftServer server,
@NotNull ServerPlayer player,
@NotNull ValueInput loadedData
) {
// See PlayerList#placeNewPlayer
World bukkitWorld;
Optional<Long> msbs = loadedData.getLong("WorldUUIDMost");
@@ -184,7 +192,8 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
ServerLevel level = server.getLevel(Level.OVERWORLD);
if (level != null) {
// Adjust player to default spawn (in keeping with Paper handling) when world not found.
player.snapTo(player.adjustSpawnLocation(level, level.getSharedSpawnPos()).getBottomCenter(), level.getSharedSpawnAngle(), 0.0F);
LevelData.RespawnData respawnData = level.levelData.getRespawnData();
player.snapTo(player.adjustSpawnLocation(level, respawnData.pos()).getBottomCenter(), respawnData.yaw(), 0.0F);
player.spawnIn(level);
} else {
logger.warning("Tried to load player with invalid world when no fallback was available!");
@@ -208,16 +217,7 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
if (nmsPlayer.getBukkitEntity() instanceof BaseOpenPlayer openPlayer) {
return openPlayer;
}
MinecraftServer server = nmsPlayer.getServer();
if (server == null) {
if (!(Bukkit.getServer() instanceof CraftServer craftServer)) {
logger.warning(() ->
"Unable to inject ServerPlayer, certain player data may be lost when saving! Server is not a CraftServer: "
+ Bukkit.getServer().getClass().getName());
return player;
}
server = craftServer.getServer();
}
MinecraftServer server = nmsPlayer.level().getServer();
injectPlayer(server, nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
@@ -244,7 +244,7 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
// See net.minecraft.server.level.ServerPlayer#openMenu(MenuProvider)
AbstractContainerMenu menu;
Component title;
if (inventory instanceof OpenInventory playerInv) {
if (inventory instanceof BaseOpenInventory playerInv) {
menu = playerInv.createMenu(player, player.nextContainerCounter(), viewOnly);
title = playerInv.getTitle(player);
} else if (inventory instanceof OpenEnderChest enderChest) {
+1
View File
@@ -21,6 +21,7 @@ dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
implementation(project(":openinvadaptercommon"))
implementation(project(":openinvadapterpaper1_21_8"))
implementation(project(":openinvadapterpaper1_21_5"))
implementation(project(":openinvadapterpaper1_21_4"))
implementation(project(":openinvadapterpaper1_21_3"))
+1
View File
@@ -21,6 +21,7 @@ dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
implementation(project(":openinvadaptercommon"))
implementation(project(":openinvadapterpaper1_21_8"))
implementation(project(":openinvadapterpaper1_21_5"))
implementation(project(":openinvadapterpaper1_21_4"))
+1
View File
@@ -21,6 +21,7 @@ dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
implementation(project(":openinvadaptercommon"))
implementation(project(":openinvadapterpaper1_21_8"))
implementation(project(":openinvadapterpaper1_21_5"))
paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT")
@@ -23,7 +23,7 @@ import org.bukkit.event.inventory.InventoryType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class OpenInventory extends com.lishid.openinv.internal.common.container.OpenInventory {
public class OpenInventory extends com.lishid.openinv.internal.paper1_21_8.container.OpenInventory {
public OpenInventory(@NotNull org.bukkit.entity.Player bukkitPlayer) {
super(bukkitPlayer);
+1
View File
@@ -21,6 +21,7 @@ dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
implementation(project(":openinvadaptercommon"))
implementation(project(":openinvadapterpaper1_21_8"))
paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT")
}
@@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.logging.Level;
import java.util.logging.Logger;
public class InternalAccessor extends com.lishid.openinv.internal.common.InternalAccessor {
public class InternalAccessor extends com.lishid.openinv.internal.paper1_21_8.InternalAccessor {
private final @NotNull PlayerManager manager;
@@ -16,7 +16,7 @@ import java.util.Optional;
import java.util.UUID;
import java.util.logging.Logger;
public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager {
public class PlayerManager extends com.lishid.openinv.internal.paper1_21_8.player.PlayerManager {
public PlayerManager(@NotNull Logger logger) {
super(logger);
+26
View File
@@ -0,0 +1,26 @@
plugins {
`openinv-base`
alias(libs.plugins.paperweight)
}
configurations.all {
resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") {
val paper = candidates.firstOrNull {
it.id.let { id ->
id is ModuleComponentIdentifier && id.module == "paper-api"
}
}
if (paper != null) {
select(paper)
}
because("module is written for Paper servers")
}
}
dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
implementation(project(":openinvadaptercommon"))
paperweight.paperDevBundle("1.21.8-R0.1-SNAPSHOT")
}
@@ -0,0 +1,31 @@
package com.lishid.openinv.internal.paper1_21_8;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.internal.paper1_21_8.container.OpenInventory;
import com.lishid.openinv.internal.paper1_21_8.player.PlayerManager;
import com.lishid.openinv.util.lang.LanguageManager;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.logging.Logger;
public class InternalAccessor extends com.lishid.openinv.internal.common.InternalAccessor {
private final @NotNull PlayerManager manager;
public InternalAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
super(logger, lang);
manager = new PlayerManager(logger);
}
@Override
public @NotNull PlayerManager getPlayerManager() {
return manager;
}
@Override
public @NotNull ISpecialPlayerInventory createPlayerInventory(@NotNull Player player) {
return new OpenInventory(player);
}
}
@@ -0,0 +1,43 @@
package com.lishid.openinv.internal.paper1_21_8.container;
import com.lishid.openinv.internal.common.container.BaseOpenInventory;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class OpenInventory extends BaseOpenInventory {
public OpenInventory(@NotNull Player bukkitPlayer) {
super(bukkitPlayer);
}
@Override
public @NotNull Component getTitle(@Nullable ServerPlayer viewer) {
MutableComponent component = Component.empty();
// Prefix for use with custom bitmap image fonts.
if (owner.equals(viewer)) {
component.append(
Component.translatableWithFallback("openinv.container.inventory.self", "")
.withStyle(style -> style
.withFont(ResourceLocation.parse("openinv:font/inventory"))
.withColor(ChatFormatting.WHITE)));
} else {
component.append(
Component.translatableWithFallback("openinv.container.inventory.other", "")
.withStyle(style -> style
.withFont(ResourceLocation.parse("openinv:font/inventory"))
.withColor(ChatFormatting.WHITE)));
}
// Normal title: "Inventory - OwnerName"
component.append(Component.translatableWithFallback("openinv.container.inventory.prefix", "", owner.getName()))
.append(Component.translatable("container.inventory"))
.append(Component.translatableWithFallback("openinv.container.inventory.suffix", " - %s", owner.getName()));
return component;
}
}
@@ -0,0 +1,66 @@
package com.lishid.openinv.internal.paper1_21_8.player;
import com.lishid.openinv.event.OpenEvents;
import com.lishid.openinv.internal.common.player.BaseOpenPlayer;
import com.mojang.logging.LogUtils;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.server.level.ServerPlayer;
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.slf4j.Logger;
import java.nio.file.Files;
import java.nio.file.Path;
public class OpenPlayer extends BaseOpenPlayer {
protected OpenPlayer(
CraftServer server,
ServerPlayer entity,
PlayerManager manager
) {
super(server, entity, manager);
}
@Override
public void saveData() {
if (OpenEvents.saveCancelled(this)) {
return;
}
ServerPlayer player = this.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 = isOnline()
? null
: worldNBTStorage.load(player.getName().getString(), player.getStringUUID(), scopedCollector).orElse(null);
CompoundTag playerData = getWritableTag(oldData);
ValueOutput valueOutput = TagValueOutput.createWrappingWithContext(scopedCollector, player.registryAccess(), playerData);
player.saveWithoutId(valueOutput);
if (oldData != null) {
// Revert certain special data values when offline.
revertSpecialValues(playerData, oldData);
}
Path playerDataDir = worldNBTStorage.getPlayerDir().toPath();
Path tempFile = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
NbtIo.writeCompressed(playerData, tempFile);
Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
Util.safeReplaceFile(dataFile, tempFile, backupFile);
} catch (Exception e) {
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
}
}
}
@@ -0,0 +1,100 @@
package com.lishid.openinv.internal.paper1_21_8.player;
import com.lishid.openinv.internal.common.player.BaseOpenPlayer;
import com.lishid.openinv.util.JulLoggerAdapter;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.storage.ValueInput;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.logging.Logger;
public class PlayerManager extends com.lishid.openinv.internal.common.player.PlayerManager {
public PlayerManager(@NotNull Logger logger) {
super(logger);
}
@Override
protected boolean loadData(@NotNull MinecraftServer server, @NotNull ServerPlayer player) {
// See CraftPlayer#loadData
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), new JulLoggerAdapter(logger))) {
ValueInput loadedData = server.getPlayerList().playerIo.load(player, scopedCollector).orElse(null);
if (loadedData == null) {
// Exceptions with loading are logged.
return false;
}
// Read basic data into the player.
player.load(loadedData);
// Game type settings are loaded separately.
player.loadGameTypes(loadedData);
// World is not loaded by ServerPlayer#load(CompoundTag) on Paper.
parseWorld(server, player, loadedData);
}
return true;
}
@Override
protected void spawnInDefaultWorld(@NotNull MinecraftServer server, @NotNull 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.
player.snapTo(player.adjustSpawnLocation(level, level.getSharedSpawnPos()).getBottomCenter(), level.getSharedSpawnAngle(), 0.0F);
player.spawnIn(level);
} else {
logger.warning("Tried to load player with invalid world when no fallback was available!");
}
}
@Override
protected void injectPlayer(@NotNull MinecraftServer server, @NotNull ServerPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new OpenPlayer(server.server, player, this));
}
@Override
public @NotNull Player inject(@NotNull Player player) {
try {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.getBukkitEntity() instanceof BaseOpenPlayer openPlayer) {
return openPlayer;
}
MinecraftServer server = nmsPlayer.getServer();
if (server == null) {
if (!(Bukkit.getServer() instanceof CraftServer craftServer)) {
logger.warning(() ->
"Unable to inject ServerPlayer, certain player data may be lost when saving! Server is not a CraftServer: "
+ Bukkit.getServer().getClass().getName());
return player;
}
server = craftServer.getServer();
}
injectPlayer(server, nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
logger.log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!"
);
return player;
}
}
}
+2 -2
View File
@@ -10,9 +10,9 @@ plugins {
apply<SpigotSetup>()
apply<SpigotReobf>()
val spigotVer = "1.21.7-R0.1-SNAPSHOT"
val spigotVer = "1.21.10-R0.1-SNAPSHOT"
// Used by common adapter to relocate Craftbukkit classes to a versioned package.
rootProject.extra["craftbukkitPackage"] = "v1_21_R5"
rootProject.extra["craftbukkitPackage"] = "v1_21_R6"
configurations.all {
resolutionStrategy.capabilitiesResolution.withCapability("org.spigotmc:spigot-api") {
@@ -1,11 +1,11 @@
package com.lishid.openinv.internal.reobf.container.bukkit;
import com.google.common.base.Preconditions;
import com.lishid.openinv.internal.reobf.container.OpenInventory;
import com.lishid.openinv.internal.reobf.container.BaseOpenInventory;
import net.minecraft.core.NonNullList;
import net.minecraft.world.entity.player.Inventory;
import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftInventory;
import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftInventory;
import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
@@ -15,13 +15,13 @@ import org.jetbrains.annotations.Nullable;
public class OpenPlayerInventory extends CraftInventory implements PlayerInventory {
public OpenPlayerInventory(@NotNull OpenInventory inventory) {
public OpenPlayerInventory(@NotNull BaseOpenInventory inventory) {
super(inventory);
}
@Override
public @NotNull OpenInventory getInventory() {
return (OpenInventory) super.getInventory();
public @NotNull BaseOpenInventory getInventory() {
return (BaseOpenInventory) super.getInventory();
}
@Override
@@ -6,8 +6,8 @@ import net.minecraft.world.Container;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.v1_21_R5.CraftEquipmentSlot;
import org.bukkit.craftbukkit.v1_21_R5.inventory.CraftItemStack;
import org.bukkit.craftbukkit.v1_21_R6.CraftEquipmentSlot;
import org.bukkit.craftbukkit.v1_21_R6.inventory.CraftItemStack;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
@@ -11,12 +11,10 @@ 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.TagValueInput;
import net.minecraft.world.level.storage.TagValueOutput;
import net.minecraft.world.level.storage.ValueInput;
import net.minecraft.world.level.storage.ValueOutput;
import org.bukkit.craftbukkit.v1_21_R5.CraftServer;
import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_R6.CraftServer;
import org.bukkit.craftbukkit.v1_21_R6.entity.CraftPlayer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -108,17 +106,9 @@ public class OpenPlayer extends CraftPlayer {
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), logger)) {
PlayerDataStorage worldNBTStorage = server.getServer().getPlayerList().playerIo;
ValueInput oldDataInput = isOnline()
CompoundTag oldData = isOnline()
? null
: worldNBTStorage.load(player.getName().getString(), player.getStringUUID(), scopedCollector, player.registryAccess()).orElse(null);
// Grab CompoundTag out of TagValueInput - we can't iterate over the fields via TagValueInput.
CompoundTag oldData = null;
if (oldDataInput != null) {
Field tagValueInputInput = TagValueInput.class.getDeclaredField("input");
tagValueInputInput.setAccessible(true);
oldData = (CompoundTag) tagValueInputInput.get(oldDataInput);
}
: worldNBTStorage.load(player.nameAndId()).orElse(null);
CompoundTag playerData = getWritableTag(oldData);
ValueOutput valueOutput = TagValueOutput.createWithContext(scopedCollector, player.registryAccess());
@@ -5,6 +5,7 @@ import com.lishid.openinv.internal.reobf.container.OpenEnderChest;
import com.lishid.openinv.internal.reobf.container.OpenInventory;
import com.lishid.openinv.util.JulLoggerAdapter;
import com.mojang.authlib.GameProfile;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
@@ -17,13 +18,14 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.storage.TagValueInput;
import net.minecraft.world.level.storage.ValueInput;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_21_R5.CraftServer;
import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_R5.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_21_R6.CraftServer;
import org.bukkit.craftbukkit.v1_21_R6.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_21_R6.event.CraftEventFactory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
@@ -138,17 +140,17 @@ public class PlayerManager implements com.lishid.openinv.internal.PlayerManager
// See CraftPlayer#loadData
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(player.problemPath(), new JulLoggerAdapter(logger))) {
ValueInput loadedData = player.server.getPlayerList().playerIo.load(player, scopedCollector).orElse(null);
CompoundTag loadedData = player.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(loadedData);
// Game type settings are loaded separately.
player.loadGameTypes(loadedData);
player.load(valueInput);
}
return true;
+1
View File
@@ -13,6 +13,7 @@ dependencies {
implementation(project(":openinvapi"))
implementation(project(":openinvcommon"))
implementation(project(":openinvadaptercommon"))
implementation(project(":openinvadapterpaper1_21_8"))
implementation(project(":openinvadapterpaper1_21_5"))
implementation(project(":openinvadapterpaper1_21_4"))
implementation(project(":openinvadapterpaper1_21_3"))
@@ -64,7 +64,7 @@ public class InternalAccessor {
}
private @Nullable Accessor getAccessor(@NotNull Logger logger, @NotNull LanguageManager lang) {
Version maxSupported = Version.of(1, 21, 7);
Version maxSupported = Version.of(1, 21, 10);
Version minSupported = Version.of(1, 21, 1);
// Ensure version is in supported range.
@@ -85,9 +85,13 @@ public class InternalAccessor {
// Paper or a Paper fork, can use Mojang-mapped internals.
if (BukkitVersions.MINECRAFT.lessThanOrEqual(maxSupported)
&& BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 6))) { // 1.21.6, 1.21.7
&& BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 9))) { // 1.21.9, 1.21.10
return new com.lishid.openinv.internal.common.InternalAccessor(logger, lang);
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 8))
&& BukkitVersions.MINECRAFT.greaterThanOrEqual(Version.of(1, 21, 6))) { // 1.21.6, 1.21.7, 1.21.8
return new com.lishid.openinv.internal.paper1_21_8.InternalAccessor(logger, lang);
}
if (BukkitVersions.MINECRAFT.equals(Version.of(1, 21, 5))) { // 1.21.5
return new com.lishid.openinv.internal.paper1_21_5.InternalAccessor(logger, lang);
}
@@ -176,7 +180,7 @@ public class InternalAccessor {
if (!PAPER) {
return getSpigotReleaseLink();
}
// Paper 1.21.1-1.21.7
// Paper 1.21.1+
return "https://github.com/Jikoo/OpenInv/releases";
}
@@ -196,6 +200,12 @@ public class InternalAccessor {
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 6))) {
return "Unsupported; upgrade to 1.21.7: https://github.com/Jikoo/OpenInv/releases";
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 8))) {
return "https://github.com/Jikoo/OpenInv/releases/tag/5.1.13";
}
if (BukkitVersions.MINECRAFT.lessThanOrEqual(Version.of(1, 21, 9))) {
return "Unsupported; upgrade to 1.21.10: https://github.com/Jikoo/OpenInv/releases";
}
return "https://github.com/Jikoo/OpenInv/releases";
}
@@ -1,13 +1,17 @@
{
"pack": {
"description": "Improve OpenInv's legibility",
"pack_format": 55,
"supported_formats": [ 34, 81 ]
"min_format": 34,
"max_format": [69, 0],
"pack_format": 64,
"supported_formats": [ 34, 69 ]
},
"overlays": {
"entries": [
{
"formats": [ 44, 81 ],
"min_format": 44,
"max_format": [69, 0],
"formats": [ 44, 69 ],
"directory": "openinv_44"
},
{
+1
View File
@@ -19,6 +19,7 @@ if (!java.lang.Boolean.getBoolean("jitpack")) {
val internals = listOf(
"common",
"paper1_21_8",
"paper1_21_5",
"paper1_21_4",
"paper1_21_3",