Skip to content

Commit

Permalink
Merge pull request #271 from MrJeremyFisher/ctplus-1.20
Browse files Browse the repository at this point in the history
CombatTagPlus 1.20.4
  • Loading branch information
okx-code committed Feb 21, 2024
2 parents 6c01a6d + 3fd00b8 commit 05dce9d
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 27 deletions.
2 changes: 1 addition & 1 deletion plugins/combattagplus-paper/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ version = "2.0.1"

dependencies {
paperweight {
paperDevBundle("1.18.2-R0.1-SNAPSHOT")
paperDevBundle("1.20.4-R0.1-SNAPSHOT")
}

compileOnly("com.github.TownyAdvanced:towny:0.97.5.0")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
package net.minelink.ctplus.nms;

import net.minecraft.network.Connection;
import net.minecraft.network.PacketListener;
import net.minecraft.network.protocol.PacketFlow;
import net.minelink.ctplus.util.EmptyChannel;

import java.lang.reflect.Field;

public class NpcNetworkManager extends Connection {
public NpcNetworkManager() {
super(PacketFlow.SERVERBOUND);
channel = new EmptyChannel(null);
}

@Override
public void setListener(PacketListener packetListener) {
// Will need to be remapped by version, See: https://mappings.cephx.dev/YOUR_VERSION/net/minecraft/network/Connection.html for mappings
Field connectionPacketListener = null;
Field connectionDisconnectListener = null;

try {
connectionPacketListener = Connection.class.getDeclaredField("q"); // net.minecraft.network.Connection.packetListener
connectionPacketListener.setAccessible(true);

connectionDisconnectListener = Connection.class.getDeclaredField("p"); // net.minecraft.network.Connection.disconnectListener
connectionDisconnectListener.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}


try {
connectionPacketListener.set(this, packetListener); // q -> private volatile PacketListener packetListener
connectionDisconnectListener.set(this, null); // p -> private volatile PacketListener disconnectListener
} catch (Throwable e) {
e.printStackTrace();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,28 @@

import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;

import net.minecraft.network.protocol.PacketFlow;
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.server.network.CommonListenerCookie;
import net.minelink.ctplus.compat.base.NpcIdentity;
import net.minelink.ctplus.compat.base.NpcNameGeneratorFactory;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;

public class NpcPlayer extends ServerPlayer {

private NpcIdentity identity;
public NpcPlayer(MinecraftServer server, ServerLevel world, GameProfile profile) {
super(server, world, profile);
public NpcPlayer(MinecraftServer server, ServerLevel world, GameProfile profile, ClientInformation clientInformation) {
super(server, world, profile, clientInformation);
}

public NpcIdentity getNpcIdentity() {
Expand All @@ -28,12 +34,13 @@ public static NpcPlayer valueOf(Player player) {
MinecraftServer minecraftServer = MinecraftServer.getServer();
ServerLevel worldServer = ((CraftWorld) player.getWorld()).getHandle();
GameProfile gameProfile = new GameProfile(player.getUniqueId(), NpcNameGeneratorFactory.getNameGenerator().generate(player));
ClientInformation clientInformation = ((CraftPlayer) player).getHandle().clientInformation();

for (Map.Entry<String, Property> entry: ((CraftPlayer) player).getProfile().getProperties().entries()) {
gameProfile.getProperties().put(entry.getKey(), entry.getValue());
}

NpcPlayer npcPlayer = new NpcPlayer(minecraftServer, worldServer, gameProfile);
NpcPlayer npcPlayer = new NpcPlayer(minecraftServer, worldServer, gameProfile, clientInformation);
npcPlayer.identity = new NpcIdentity(player);

new NpcPlayerConnection(npcPlayer);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package net.minelink.ctplus.nms;

import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.CommonListenerCookie;
import net.minecraft.server.network.ServerGamePacketListenerImpl;

public class NpcPlayerConnection extends ServerGamePacketListenerImpl {
public NpcPlayerConnection(ServerPlayer player) {
super(MinecraftServer.getServer(), new NpcNetworkManager(), player);
super(MinecraftServer.getServer(), new NpcNetworkManager(), player, new CommonListenerCookie(player.gameProfile, -1, player.clientInformation()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@

import com.google.common.collect.Lists;
import com.mojang.datafixers.util.Pair;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundSetEquipmentPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
Expand All @@ -20,15 +16,22 @@
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.food.FoodData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.storage.PlayerDataStorage;
import net.minelink.ctplus.compat.base.NpcIdentity;
import net.minelink.ctplus.compat.base.NpcPlayerHelper;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;

public class NpcPlayerHelperImpl implements NpcPlayerHelper {
@Override
public Player spawn(Player player) {
Expand All @@ -44,12 +47,16 @@ public Player spawn(Player player) {
for (ServerPlayer serverPlayer : MinecraftServer.getServer().getPlayerList().getPlayers()) {
if (serverPlayer instanceof NpcPlayer) continue;

ClientboundPlayerInfoPacket packet = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.ADD_PLAYER, npcPlayer);
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, npcPlayer);
serverPlayer.connection.send(packet);
}

worldServer.entityManager.getEntityGetter().get(player.getUniqueId()).remove(Entity.RemovalReason.DISCARDED);
worldServer.entityManager.addNewEntity(npcPlayer);
Entity oldPlayer = worldServer.getEntity(player.getUniqueId());

if (oldPlayer != null) {
oldPlayer.remove(Entity.RemovalReason.DISCARDED);
worldServer.addFreshEntity(npcPlayer);
}

return npcPlayer.getBukkitEntity();
}
Expand All @@ -64,11 +71,11 @@ public void despawn(Player player) {
for (ServerPlayer serverPlayer : MinecraftServer.getServer().getPlayerList().getPlayers()) {
if (serverPlayer instanceof NpcPlayer) continue;

ClientboundPlayerInfoPacket packet = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.REMOVE_PLAYER, entity);
ClientboundPlayerInfoRemovePacket packet = new ClientboundPlayerInfoRemovePacket(List.of(entity.getUUID()));
serverPlayer.connection.send(packet);
}

ServerLevel worldServer = entity.getLevel();
ServerLevel worldServer = entity.serverLevel();
worldServer.chunkSource.removeEntity(entity);
worldServer.getPlayers(serverPlayer -> serverPlayer instanceof NpcPlayer).remove(entity);
removePlayerList(player);
Expand Down Expand Up @@ -98,7 +105,7 @@ public void updateEquipment(Player player) {

for (EquipmentSlot slot : EquipmentSlot.values()) {
ItemStack item = entity.getItemBySlot(slot);
if (item == null) continue;
if (item.getItem() == Items.AIR) continue;

// Set the attribute for this equipment to consider armor values and enchantments
// Actually getAttributeMap().a() is used with the previous item, to clear the Attributes
Expand All @@ -109,7 +116,7 @@ public void updateEquipment(Player player) {
List<Pair<EquipmentSlot, ItemStack>> list = Lists.newArrayList();
list.add(Pair.of(slot, item));
Packet packet = new ClientboundSetEquipmentPacket(entity.getId(), list);
entity.getLevel().chunkSource.broadcast(entity, packet);
entity.serverLevel().chunkSource.broadcast(entity, packet);
}
}

Expand Down Expand Up @@ -154,7 +161,7 @@ public void syncOffline(Player player) {
playerNbt.putInt("foodTickTimer", foodTickTimer);
playerNbt.putFloat("foodSaturationLevel", entity.getFoodData().getSaturationLevel());
playerNbt.putFloat("foodExhaustionLevel", entity.getFoodData().exhaustionLevel);
playerNbt.putShort("Fire", (short) entity.remainingFireTicks);
playerNbt.putShort("Fire", (short) entity.getRemainingFireTicks());
playerNbt.put("Inventory", npcPlayer.getInventory().save(new ListTag()));

File file1 = new File(worldStorage.getPlayerDir(), identity.getId().toString() + ".dat.tmp");
Expand All @@ -176,8 +183,8 @@ public void createPlayerList(Player player) {
ServerPlayer p = ((CraftPlayer) player).getHandle();

for (ServerPlayer serverPlayer : MinecraftServer.getServer().getPlayerList().getPlayers()) {
ClientboundPlayerInfoPacket packet = new ClientboundPlayerInfoPacket(
ClientboundPlayerInfoPacket.Action.ADD_PLAYER, serverPlayer);
ClientboundPlayerInfoUpdatePacket packet = new ClientboundPlayerInfoUpdatePacket(
ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, serverPlayer);
p.connection.send(packet);
}
}
Expand All @@ -186,7 +193,7 @@ public void createPlayerList(Player player) {
public void removePlayerList(Player player) {
ServerPlayer p = ((CraftPlayer) player).getHandle();
for (ServerPlayer serverPlayer : MinecraftServer.getServer().getPlayerList().getPlayers()) {
ClientboundPlayerInfoPacket packet = new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.REMOVE_PLAYER, serverPlayer);
ClientboundPlayerInfoRemovePacket packet = new ClientboundPlayerInfoRemovePacket(List.of(serverPlayer.getUUID()));
p.connection.send(packet);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package net.minelink.ctplus.util;


import io.netty.channel.AbstractChannel;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.EventLoop;

import java.net.SocketAddress;

/**
* Fake connection channel for NPC players
*/
public class EmptyChannel extends AbstractChannel {
private final ChannelConfig config = new DefaultChannelConfig(this);

public EmptyChannel(Channel parent) {
super(parent);
}

@Override
public ChannelConfig config() {
config.setAutoRead(true);
return config;
}

@Override
protected void doBeginRead() throws Exception {
}

@Override
protected void doBind(SocketAddress arg0) throws Exception {
}

@Override
protected void doClose() throws Exception {
}

@Override
protected void doDisconnect() throws Exception {
}

@Override
protected void doWrite(ChannelOutboundBuffer arg0) throws Exception {
}

@Override
public boolean isActive() {
return false;
}

@Override
protected boolean isCompatible(EventLoop arg0) {
return false;
}

@Override
public boolean isOpen() {
return false;
}

@Override
protected SocketAddress localAddress0() {
return null;
}

@Override
public ChannelMetadata metadata() {
return new ChannelMetadata(true);
}

@Override
protected AbstractUnsafe newUnsafe() {
return null;
}

@Override
protected SocketAddress remoteAddress0() {
return null;
}
}
2 changes: 1 addition & 1 deletion plugins/combattagplus-paper/src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version: ${version}
authors: [Byteflux, Sudzzy]
main: net.minelink.ctplus.CombatTagPlus
softdepend: [BarAPI, PlayerHeads, WorldGuard, Factions, Towny, BossBarAPI, ActionBarAPI, mcMMO]
api-version: 1.14
api-version: 1.20
commands:
combattagplus:
aliases: [ctplus, ct, combattag]
Expand Down

0 comments on commit 05dce9d

Please sign in to comment.