Skip to content

Commit

Permalink
Add support for 1.20.4 (#173)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jikoo authored Dec 29, 2023
1 parent 859179d commit 78354fd
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 69 deletions.
6 changes: 3 additions & 3 deletions internal/v1_20_R1/pom.xml → internal/v1_20_R3/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@
<version>4.4.2-SNAPSHOT</version>
</parent>

<artifactId>openinvadapter1_20_R1</artifactId>
<name>OpenInvAdapter1_20_R1</name>
<artifactId>openinvadapter1_20_R3</artifactId>
<name>OpenInvAdapter1_20_R3</name>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spigot.version>1.20.1-R0.1-SNAPSHOT</spigot.version>
<spigot.version>1.20.4-R0.1-SNAPSHOT</spigot.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.lishid.openinv.internal.v1_20_R1;
package com.lishid.openinv.internal.v1_20_R3;

import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.util.ReflectionHelper;
import java.lang.reflect.Field;
import java.util.logging.Logger;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
Expand Down Expand Up @@ -49,11 +47,14 @@
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.ShulkerBox;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Field;
import java.util.logging.Logger;

public class AnySilentContainer implements IAnySilentContainer {

private @Nullable Field serverPlayerGameModeGameType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.lishid.openinv.internal.v1_20_R1;
package com.lishid.openinv.internal.v1_20_R3;

import com.mojang.logging.LogUtils;
import net.minecraft.Util;
Expand All @@ -25,12 +25,13 @@
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.storage.PlayerDataStorage;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;

public class OpenPlayer extends CraftPlayer {

Expand All @@ -40,11 +41,7 @@ public OpenPlayer(CraftServer server, ServerPlayer entity) {

@Override
public void loadData() {
// See CraftPlayer#loadData
CompoundTag loaded = this.server.getHandle().playerIo.load(this.getHandle());
if (loaded != null) {
getHandle().readAdditionalSaveData(loaded);
}
PlayerDataManager.loadData(getHandle());
}

@Override
Expand All @@ -63,11 +60,12 @@ public void saveData() {
revertSpecialValues(playerData, oldData);
}

File file = File.createTempFile(player.getStringUUID() + "-", ".dat", worldNBTStorage.getPlayerDir());
Path playerDataDir = worldNBTStorage.getPlayerDir().toPath();
Path file = Files.createTempFile(playerDataDir, player.getStringUUID() + "-", ".dat");
NbtIo.writeCompressed(playerData, file);
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat");
File file2 = new File(worldNBTStorage.getPlayerDir(), player.getStringUUID() + ".dat_old");
Util.safeReplaceFile(file1, file, file2);
Path dataFile = playerDataDir.resolve(player.getStringUUID() + ".dat");
Path backupFile = playerDataDir.resolve(player.getStringUUID() + ".dat_old");
Util.safeReplaceFile(dataFile, file, backupFile);
} catch (Exception e) {
LogUtils.getLogger().warn("Failed to save player data for {}: {}", player.getScoreboardName(), e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,47 +14,68 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.lishid.openinv.internal.v1_20_R1;
package com.lishid.openinv.internal.v1_20_R3;

import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.OpenInventoryView;
import com.mojang.authlib.GameProfile;
import java.lang.reflect.Field;
import java.util.logging.Logger;
import com.mojang.serialization.Dynamic;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.dimension.DimensionType;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R1.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftContainer;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftContainer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Field;
import java.util.UUID;
import java.util.logging.Logger;

public class PlayerDataManager implements IPlayerDataManager {

private static boolean paper;

static {
try {
Class.forName("io.papermc.paper.configuration.Configuration");
paper = true;
} catch (ClassNotFoundException ignored) {
paper = false;
}
}

private @Nullable Field bukkitEntity;

public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
Logger logger = OpenInv.getPlugin(OpenInv.class).getLogger();
logger.warning("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
Logger logger = JavaPlugin.getPlugin(OpenInv.class).getLogger();
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;
}
Expand All @@ -74,7 +95,7 @@ public PlayerDataManager() {

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 provided Player implementation");
throw new RuntimeException("Unable to fetch EntityPlayer from Player implementation " + player.getClass().getName());
}

return nmsPlayer;
Expand All @@ -87,51 +108,110 @@ public PlayerDataManager() {
return null;
}

// Create a profile and entity to load the player data
// See net.minecraft.server.players.PlayerList#canPlayerLogin
// and net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
ServerLevel worldServer = server.getLevel(Level.OVERWORLD);

if (worldServer == null) {
return null;
}

ServerPlayer entity = new ServerPlayer(server, worldServer, profile);
// Create a new ServerPlayer.
ServerPlayer entity = createNewPlayer(server, worldServer, offline);

// Stop listening for advancement progression - if this is not cleaned up, loading causes a memory leak.
entity.getAdvancements().stopListening();

// Try to load the player's data.
if (loadData(entity)) {
// If data is loaded successfully, return the Bukkit entity.
return entity.getBukkitEntity();
}

return null;
}

private @NotNull ServerPlayer createNewPlayer(
@NotNull MinecraftServer server,
@NotNull ServerLevel worldServer,
@NotNull final OfflinePlayer offline) {
// See net.minecraft.server.players.PlayerList#canPlayerLogin(ServerLoginPacketListenerImpl, GameProfile)
// See net.minecraft.server.network.ServerLoginPacketListenerImpl#handleHello(ServerboundHelloPacket)
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());

ClientInformation dummyInfo = new ClientInformation(
"en_us",
1, // Reduce distance just in case.
ChatVisiblity.HIDDEN, // Don't accept chat.
false,
ServerPlayer.DEFAULT_MODEL_CUSTOMIZATION,
ServerPlayer.DEFAULT_MAIN_HAND,
true,
false // Don't list in player list (not that this player is in the list anyway).
);

ServerPlayer entity = new ServerPlayer(server, worldServer, profile, dummyInfo);

try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
}

// Load data. This also reads basic data into the player.
return entity;
}

static boolean loadData(@NotNull ServerPlayer player) {
// See CraftPlayer#loadData
CompoundTag loadedData = server.getPlayerList().playerIo.load(entity);
CompoundTag loadedData = player.server.getPlayerList().playerIo.load(player);

if (loadedData == null) {
// Exceptions with loading are logged by Mojang.
return null;
return false;
}

// Read basic data into the player.
player.load(loadedData);
// Also read "extra" data.
entity.readAdditionalSaveData(loadedData);
player.readAdditionalSaveData(loadedData);

if (entity.level() == null) {
// Paper: Move player to spawn
entity.spawnIn(null);
if (paper) {
// Paper: world is not loaded by ServerPlayer#load(CompoundTag).
parseWorld(player, loadedData);
}

// Return the Bukkit entity.
return entity.getBukkitEntity();
return true;
}

private static void parseWorld(@NotNull ServerPlayer player, @NotNull CompoundTag loadedData) {
// See PlayerList#placeNewPlayer
World bukkitWorld;
if (loadedData.contains("WorldUUIDMost") && loadedData.contains("WorldUUIDLeast")) {
// Modern Bukkit world.
bukkitWorld = Bukkit.getServer().getWorld(new UUID(loadedData.getLong("WorldUUIDMost"), loadedData.getLong("WorldUUIDLeast")));
} else if (loadedData.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) {
// Legacy Bukkit world.
bukkitWorld = Bukkit.getServer().getWorld(loadedData.getString("world"));
} else {
// Vanilla player data.
DimensionType.parseLegacy(new Dynamic<>(NbtOps.INSTANCE, loadedData.get("Dimension")))
.resultOrPartial(JavaPlugin.getPlugin(OpenInv.class).getLogger()::warning)
.map(player.server::getLevel)
// If ServerLevel exists, set, otherwise move to spawn.
.ifPresentOrElse(player::setServerLevel, () -> player.spawnIn(null));
return;
}
if (bukkitWorld == null) {
player.spawnIn(null);
return;
}
player.setServerLevel(((CraftWorld) bukkitWorld).getHandle());
}

void injectPlayer(ServerPlayer player) throws IllegalAccessException {
private void injectPlayer(ServerPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
Expand All @@ -141,9 +221,8 @@ void injectPlayer(ServerPlayer player) throws IllegalAccessException {
bukkitEntity.set(player, new OpenPlayer(player.server.server, player));
}

@NotNull
@Override
public Player inject(@NotNull Player player) {
public @NotNull Player inject(@NotNull Player player) {
try {
ServerPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.getBukkitEntity() instanceof OpenPlayer openPlayer) {
Expand All @@ -152,14 +231,16 @@ public Player inject(@NotNull Player player) {
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
JavaPlugin.getPlugin(OpenInv.class).getLogger().log(
java.util.logging.Level.WARNING,
e,
() -> "Unable to inject ServerPlayer, certain player data may be lost when saving!");
return player;
}
}

@Nullable
@Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {

ServerPlayer nmsPlayer = getHandle(player);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package com.lishid.openinv.internal.v1_20_R1;
package com.lishid.openinv.internal.v1_20_R3;

import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
Expand All @@ -33,13 +30,17 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.EnderChestBlockEntity;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftInventory;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class SpecialEnderChest extends PlayerEnderChestContainer implements ISpecialEnderChest {

private final CraftInventory inventory;
Expand Down
Loading

0 comments on commit 78354fd

Please sign in to comment.