Compare commits
2 commits
d804fb1207
...
f72d5c0948
Author | SHA1 | Date | |
---|---|---|---|
f72d5c0948 | |||
fe33a003ea |
17 changed files with 334 additions and 152 deletions
|
@ -2,6 +2,7 @@ plugins {
|
|||
id("java")
|
||||
id("com.gradleup.shadow") version "8.3.2"
|
||||
id("xyz.jpenilla.run-paper") version "2.3.0"
|
||||
id("io.papermc.paperweight.userdev") version "1.7.4"
|
||||
}
|
||||
|
||||
group = "xyz.ineanto"
|
||||
|
@ -38,12 +39,13 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT")
|
||||
compileOnly("com.github.dmulloy2:ProtocolLib:-SNAPSHOT")
|
||||
paperweight.paperDevBundle("1.21.3-R0.1-SNAPSHOT")
|
||||
|
||||
compileOnly("com.github.dmulloy2:ProtocolLib:5.3.0")
|
||||
compileOnly("me.clip:placeholderapi:2.11.5")
|
||||
compileOnly("net.kyori:adventure-api:4.17.0")
|
||||
|
||||
implementation("xyz.xenondevs.invui:invui:1.36")
|
||||
implementation("xyz.xenondevs.invui:invui:1.39")
|
||||
implementation("net.wesjd:anvilgui:1.10.2-SNAPSHOT")
|
||||
implementation("com.github.jsixface:yamlconfig:1.2")
|
||||
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.15.2")
|
||||
|
@ -54,7 +56,7 @@ dependencies {
|
|||
implementation("com.google.code.gson:gson:2.10.1")
|
||||
implementation("org.bstats:bstats-bukkit:3.0.2")
|
||||
|
||||
testImplementation("com.github.MockBukkit:MockBukkit:v3.99.1")
|
||||
testImplementation("com.github.MockBukkit:MockBukkit:v3.133.2")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.10.2")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.10.2")
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package xyz.ineanto.nicko;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bukkit.Bukkit;
|
||||
|
@ -11,7 +10,6 @@ import xyz.ineanto.nicko.appearance.random.RandomNameFetcher;
|
|||
import xyz.ineanto.nicko.command.NickoCommand;
|
||||
import xyz.ineanto.nicko.config.Configuration;
|
||||
import xyz.ineanto.nicko.config.ConfigurationManager;
|
||||
import xyz.ineanto.nicko.debug.RespawnPacketListener;
|
||||
import xyz.ineanto.nicko.event.PlayerJoinListener;
|
||||
import xyz.ineanto.nicko.event.PlayerQuitListener;
|
||||
import xyz.ineanto.nicko.language.CustomLanguage;
|
||||
|
@ -134,8 +132,6 @@ public class Nicko extends JavaPlugin {
|
|||
getServer().getPluginManager().registerEvents(new PlayerJoinListener(), this);
|
||||
getServer().getPluginManager().registerEvents(new PlayerQuitListener(), this);
|
||||
metrics = new Metrics(this, 20483);
|
||||
|
||||
ProtocolLibrary.getProtocolManager().addPacketListener(new RespawnPacketListener());
|
||||
}
|
||||
|
||||
getLogger().info("Nicko has been enabled.");
|
||||
|
|
|
@ -114,7 +114,7 @@ public class AnvilManager {
|
|||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) { return Collections.singletonList(AnvilGUI.ResponseAction.close()); }
|
||||
|
||||
final ActionResult actionResult = appearanceManager.updatePlayer(skinChange, false);
|
||||
final ActionResult actionResult = appearanceManager.update(skinChange, false);
|
||||
if (!actionResult.isError()) {
|
||||
player.sendMessage(playerLanguage.translate(LanguageKey.Event.Appearance.Set.OK, true));
|
||||
} else {
|
||||
|
|
|
@ -1,36 +1,27 @@
|
|||
package xyz.ineanto.nicko.appearance;
|
||||
|
||||
import com.comphenix.protocol.wrappers.*;
|
||||
import com.google.common.collect.Multimap;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.mojang.MojangAPI;
|
||||
import xyz.ineanto.nicko.mojang.MojangSkin;
|
||||
import xyz.ineanto.nicko.packet.InternalPacketSender;
|
||||
import xyz.ineanto.nicko.packet.PacketSender;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.PlayerDataStore;
|
||||
import xyz.ineanto.nicko.storage.name.PlayerNameStore;
|
||||
import xyz.ineanto.nicko.wrapper.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class AppearanceManager {
|
||||
private final Nicko instance = Nicko.getInstance();
|
||||
private final PlayerDataStore dataStore = instance.getDataStore();
|
||||
private final PlayerNameStore nameStore = instance.getNameStore();
|
||||
private final PacketSender packetSender;
|
||||
|
||||
private final Player player;
|
||||
|
||||
public AppearanceManager(Player player) {
|
||||
this.player = player;
|
||||
this.packetSender = new InternalPacketSender(player, getNickoProfile());
|
||||
}
|
||||
|
||||
public ActionResult reset() {
|
||||
|
@ -40,7 +31,7 @@ public class AppearanceManager {
|
|||
profile.setSkin(defaultName);
|
||||
dataStore.getCache().cache(player.getUniqueId(), profile);
|
||||
|
||||
final ActionResult result = updatePlayer(true, true);
|
||||
final ActionResult result = update(true, true);
|
||||
if (!result.isError()) {
|
||||
profile.setName(null);
|
||||
profile.setSkin(null);
|
||||
|
@ -49,108 +40,29 @@ public class AppearanceManager {
|
|||
return result;
|
||||
}
|
||||
|
||||
public ActionResult updatePlayer(boolean skinChange, boolean reset) {
|
||||
public ActionResult update(boolean skinChange, boolean reset) {
|
||||
final NickoProfile profile = getNickoProfile();
|
||||
final String displayName = profile.getName() == null ? player.getName() : profile.getName();
|
||||
final WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player).withName(displayName);
|
||||
final ActionResult result = updateGameProfileSkin(gameProfile, skinChange, reset);
|
||||
if (!result.isError()) {
|
||||
updateMetadata();
|
||||
updateTabList(gameProfile, displayName);
|
||||
|
||||
final ActionResult result = packetSender.sendGameProfileUpdate(displayName, skinChange, reset);
|
||||
|
||||
if (result.isError()) { reset(); }
|
||||
|
||||
packetSender.sendEntityMetadataUpdate();
|
||||
packetSender.sendTabListUpdate(displayName);
|
||||
respawnPlayer();
|
||||
respawnEntityForOthers();
|
||||
}
|
||||
packetSender.sendEntityRespawn();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public ActionResult updateForOthers(boolean skinChange, boolean reset) {
|
||||
final NickoProfile profile = getNickoProfile();
|
||||
final String displayName = profile.getName() == null ? player.getName() : profile.getName();
|
||||
final WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player).withName(displayName);
|
||||
final ActionResult result = updateGameProfileSkin(gameProfile, skinChange, reset);
|
||||
if (!result.isError()) {
|
||||
updateMetadata();
|
||||
updateTabList(gameProfile, displayName);
|
||||
respawnEntityForOthers();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private NickoProfile getNickoProfile() {
|
||||
final Optional<NickoProfile> optionalProfile = dataStore.getData(player.getUniqueId());
|
||||
return optionalProfile.orElse(NickoProfile.EMPTY_PROFILE.clone());
|
||||
}
|
||||
|
||||
public void respawnEntityForOthers() {
|
||||
final NickoProfile nickoProfile = getNickoProfile();
|
||||
if (!nickoProfile.hasData()) return;
|
||||
|
||||
final WrapperPlayServerEntityDestroy destroy = new WrapperPlayServerEntityDestroy();
|
||||
final WrapperPlayServerSpawnEntity spawn = new WrapperPlayServerSpawnEntity();
|
||||
destroy.setEntityIds(IntList.of(player.getEntityId()));
|
||||
spawn.setEntityId(player.getEntityId());
|
||||
spawn.setLocation(player.getLocation());
|
||||
spawn.setPlayerId(player.getUniqueId());
|
||||
Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> {
|
||||
destroy.sendPacket(receiver);
|
||||
spawn.sendPacket(receiver);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private ActionResult updateGameProfileSkin(WrappedGameProfile gameProfile, boolean skinChange, boolean reset) {
|
||||
final NickoProfile profile = getNickoProfile();
|
||||
|
||||
if (skinChange) {
|
||||
Optional<MojangSkin> skin;
|
||||
try {
|
||||
final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI();
|
||||
final Optional<String> uuid = mojangAPI.getUUID(profile.getSkin());
|
||||
if (uuid.isPresent()) {
|
||||
skin = reset ? mojangAPI.getSkinWithoutCaching(uuid.get()) : mojangAPI.getSkin(uuid.get());
|
||||
if (skin.isPresent()) {
|
||||
final MojangSkin skinResult = skin.get();
|
||||
final Multimap<String, WrappedSignedProperty> properties = gameProfile.getProperties();
|
||||
properties.get("textures").clear();
|
||||
properties.put("textures", new WrappedSignedProperty("textures", skinResult.value(), skinResult.signature()));
|
||||
} else {
|
||||
reset();
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_SKIN);
|
||||
}
|
||||
} else {
|
||||
reset();
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
|
||||
}
|
||||
return ActionResult.ok();
|
||||
} catch (ExecutionException e) {
|
||||
return ActionResult.error(LanguageKey.Error.CACHE);
|
||||
} catch (IOException e) {
|
||||
reset();
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
|
||||
} catch (InterruptedException e) {
|
||||
return ActionResult.error("Unknown error");
|
||||
}
|
||||
}
|
||||
return ActionResult.ok();
|
||||
}
|
||||
|
||||
private void updateMetadata() {
|
||||
final WrappedDataWatcher entityWatcher = WrappedDataWatcher.getEntityWatcher(player);
|
||||
entityWatcher.setObject(17, (byte) 0x7f, true);
|
||||
}
|
||||
|
||||
private void respawnPlayer() {
|
||||
final World world = player.getWorld();
|
||||
final boolean wasFlying = player.isFlying();
|
||||
final boolean wasAllowedToFly = player.getAllowFlight();
|
||||
final int foodLevel = player.getFoodLevel();
|
||||
final WrapperPlayServerRespawn respawn = new WrapperPlayServerRespawn();
|
||||
respawn.setDimension(world);
|
||||
respawn.setSeed(world.getSeed());
|
||||
respawn.setGameMode(player.getGameMode());
|
||||
respawn.setPreviousGameMode(player.getGameMode());
|
||||
respawn.setCopyMetadata(true);
|
||||
respawn.sendPacket(player);
|
||||
|
||||
packetSender.sendPlayerRespawn();
|
||||
|
||||
player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||
player.setAllowFlight(wasAllowedToFly);
|
||||
player.setFlying(wasFlying);
|
||||
|
@ -159,29 +71,8 @@ public class AppearanceManager {
|
|||
player.setFoodLevel(foodLevel);
|
||||
}
|
||||
|
||||
private void updateTabList(WrappedGameProfile gameProfile, String displayName) {
|
||||
final WrapperPlayerServerPlayerInfo add = new WrapperPlayerServerPlayerInfo();
|
||||
final WrapperPlayerServerPlayerInfoRemove remove = new WrapperPlayerServerPlayerInfoRemove();
|
||||
final EnumSet<EnumWrappers.PlayerInfoAction> actions = EnumSet.of(
|
||||
EnumWrappers.PlayerInfoAction.ADD_PLAYER,
|
||||
EnumWrappers.PlayerInfoAction.INITIALIZE_CHAT,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_LISTED,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_DISPLAY_NAME,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_LATENCY);
|
||||
remove.setUUIDs(List.of(player.getUniqueId()));
|
||||
remove.broadcastPacket();
|
||||
add.setActions(actions);
|
||||
|
||||
add.setData(List.of(new PlayerInfoData(
|
||||
player.getUniqueId(),
|
||||
player.getPing(),
|
||||
true,
|
||||
EnumWrappers.NativeGameMode.fromBukkit(player.getGameMode()),
|
||||
gameProfile,
|
||||
WrappedChatComponent.fromText(displayName),
|
||||
WrappedRemoteChatSessionData.fromPlayer(player)
|
||||
)));
|
||||
add.broadcastPacket();
|
||||
private NickoProfile getNickoProfile() {
|
||||
final Optional<NickoProfile> optionalProfile = dataStore.getData(player.getUniqueId());
|
||||
return optionalProfile.orElse(NickoProfile.EMPTY_PROFILE.clone());
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ public class PlayerJoinListener implements Listener {
|
|||
if (profile.hasData()) {
|
||||
final AppearanceManager appearanceManager = new AppearanceManager(player);
|
||||
final boolean needsASkinChange = profile.getSkin() != null && !profile.getSkin().equals(player.getName());
|
||||
final ActionResult actionResult = appearanceManager.updatePlayer(needsASkinChange, false);
|
||||
final ActionResult actionResult = appearanceManager.update(needsASkinChange, false);
|
||||
if (!actionResult.isError()) {
|
||||
player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Restore.OK));
|
||||
} else {
|
||||
|
@ -68,7 +68,7 @@ public class PlayerJoinListener implements Listener {
|
|||
optionalOnlinePlayerProfile.ifPresent(profile -> {
|
||||
final AppearanceManager appearanceManager = new AppearanceManager(online);
|
||||
final boolean needsASkinChange = profile.getSkin() != null && !profile.getSkin().equals(online.getName());
|
||||
final ActionResult actionResult = appearanceManager.updateForOthers(needsASkinChange, false);
|
||||
final ActionResult actionResult = appearanceManager.update(needsASkinChange, false);
|
||||
if (actionResult.isError()) {
|
||||
logger.warning("Something wrong happened while updating players to joining player (" + actionResult.getErrorKey() + ")");
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public class RandomSkinItem {
|
|||
instance.getDataStore().updateCache(player.getUniqueId(), profile);
|
||||
|
||||
final AppearanceManager appearanceManager = new AppearanceManager(player);
|
||||
final ActionResult result = appearanceManager.updatePlayer(true, false);
|
||||
final ActionResult result = appearanceManager.update(true, false);
|
||||
if (!result.isError()) {
|
||||
player.sendMessage(playerLanguage.translate(LanguageKey.Event.Appearance.Set.OK, true));
|
||||
} else {
|
||||
|
|
125
src/main/java/xyz/ineanto/nicko/packet/InternalPacketSender.java
Normal file
125
src/main/java/xyz/ineanto/nicko/packet/InternalPacketSender.java
Normal file
|
@ -0,0 +1,125 @@
|
|||
package xyz.ineanto.nicko.packet;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundRespawnPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.mojang.MojangAPI;
|
||||
import xyz.ineanto.nicko.mojang.MojangSkin;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Look at this Mojang.
|
||||
* I want you to really stare at this code.
|
||||
* You made me do this.
|
||||
*/
|
||||
public class InternalPacketSender implements PacketSender {
|
||||
private final Player player;
|
||||
private final NickoProfile profile;
|
||||
|
||||
public InternalPacketSender(Player player, NickoProfile profile) {
|
||||
this.player = player;
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEntityRespawn() {
|
||||
if (!profile.hasData()) return;
|
||||
|
||||
final Entity entityPlayer = (Entity) player;
|
||||
|
||||
final ClientboundRemoveEntitiesPacket destroy = new ClientboundRemoveEntitiesPacket(IntList.of(player.getEntityId()));
|
||||
final ClientboundAddEntityPacket add = new ClientboundAddEntityPacket(entityPlayer, 0, entityPlayer.getOnPos());
|
||||
|
||||
Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> {
|
||||
sendPacket(destroy, player);
|
||||
sendPacket(add, player);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult sendGameProfileUpdate(String name, boolean skinChange, boolean reset) {
|
||||
final GameProfile gameProfile = ((ServerPlayer) player).gameProfile;
|
||||
|
||||
// TODO (Ineanto, 31/10/2024): Could this be refactored to get rid of the boolean?
|
||||
if (skinChange) {
|
||||
Optional<MojangSkin> skin;
|
||||
try {
|
||||
final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI();
|
||||
final Optional<String> uuid = mojangAPI.getUUID(profile.getSkin());
|
||||
if (uuid.isPresent()) {
|
||||
skin = reset ? mojangAPI.getSkinWithoutCaching(uuid.get()) : mojangAPI.getSkin(uuid.get());
|
||||
if (skin.isPresent()) {
|
||||
final MojangSkin skinResult = skin.get();
|
||||
final PropertyMap properties = gameProfile.getProperties();
|
||||
properties.get("textures").clear();
|
||||
properties.put("textures", new Property("textures", skinResult.value(), skinResult.signature()));
|
||||
} else {
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_SKIN);
|
||||
}
|
||||
} else {
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
|
||||
}
|
||||
return ActionResult.ok();
|
||||
} catch (ExecutionException e) {
|
||||
return ActionResult.error(LanguageKey.Error.CACHE);
|
||||
} catch (IOException e) {
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
|
||||
} catch (InterruptedException e) {
|
||||
return ActionResult.error("Unknown error");
|
||||
}
|
||||
}
|
||||
return ActionResult.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEntityMetadataUpdate() {
|
||||
final SynchedEntityData.DataValue<?> dataValueComponent =
|
||||
new SynchedEntityData.DataItem<>(
|
||||
new EntityDataAccessor<>(17, EntityDataSerializers.BYTE),
|
||||
(byte) 0x7f
|
||||
).value();
|
||||
|
||||
final ClientboundSetEntityDataPacket data = new ClientboundSetEntityDataPacket(player.getEntityId(), List.of(dataValueComponent));
|
||||
sendPacket(data, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPlayerRespawn() {
|
||||
final ServerPlayer serverPlayer = (ServerPlayer) player;
|
||||
final ServerLevel world = (ServerLevel) player.getWorld();
|
||||
|
||||
final ClientboundRespawnPacket respawn = new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(world), (byte) 0x03);
|
||||
sendPacket(respawn, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTabListUpdate(String displayName) {
|
||||
|
||||
}
|
||||
|
||||
private void sendPacket(Packet<?> packet, Player player) {
|
||||
((ServerPlayer) player).connection.send(packet);
|
||||
}
|
||||
}
|
15
src/main/java/xyz/ineanto/nicko/packet/PacketSender.java
Normal file
15
src/main/java/xyz/ineanto/nicko/packet/PacketSender.java
Normal file
|
@ -0,0 +1,15 @@
|
|||
package xyz.ineanto.nicko.packet;
|
||||
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
|
||||
public interface PacketSender {
|
||||
void sendEntityRespawn();
|
||||
|
||||
ActionResult sendGameProfileUpdate(String name, boolean skinChange, boolean reset);
|
||||
|
||||
void sendEntityMetadataUpdate();
|
||||
|
||||
void sendPlayerRespawn();
|
||||
|
||||
void sendTabListUpdate(String displayName);
|
||||
}
|
139
src/main/java/xyz/ineanto/nicko/packet/WrapperPacketSender.java
Normal file
139
src/main/java/xyz/ineanto/nicko/packet/WrapperPacketSender.java
Normal file
|
@ -0,0 +1,139 @@
|
|||
package xyz.ineanto.nicko.packet;
|
||||
|
||||
import com.comphenix.protocol.wrappers.*;
|
||||
import com.google.common.collect.Multimap;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.mojang.MojangAPI;
|
||||
import xyz.ineanto.nicko.mojang.MojangSkin;
|
||||
import xyz.ineanto.nicko.packet.wrapper.*;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class WrapperPacketSender implements PacketSender {
|
||||
private final Player player;
|
||||
private final NickoProfile profile;
|
||||
|
||||
private WrappedGameProfile gameProfile;
|
||||
|
||||
public WrapperPacketSender(Player player, NickoProfile profile) {
|
||||
this.player = player;
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEntityRespawn() {
|
||||
if (!profile.hasData()) return;
|
||||
|
||||
final WrapperPlayServerEntityDestroy destroy = new WrapperPlayServerEntityDestroy();
|
||||
final WrapperPlayServerSpawnEntity spawn = new WrapperPlayServerSpawnEntity();
|
||||
destroy.setEntityIds(IntList.of(player.getEntityId()));
|
||||
spawn.setEntityId(player.getEntityId());
|
||||
spawn.setLocation(player.getLocation());
|
||||
spawn.setPlayerId(player.getUniqueId());
|
||||
Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> {
|
||||
destroy.sendPacket(receiver);
|
||||
spawn.sendPacket(receiver);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult sendGameProfileUpdate(String name, boolean skinChange, boolean reset) {
|
||||
this.gameProfile = WrappedGameProfile.fromPlayer(player).withName(name);
|
||||
|
||||
// TODO (Ineanto, 31/10/2024): Could get refactored to omit this boolean?
|
||||
if (skinChange) {
|
||||
Optional<MojangSkin> skin;
|
||||
try {
|
||||
final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI();
|
||||
final Optional<String> uuid = mojangAPI.getUUID(profile.getSkin());
|
||||
if (uuid.isPresent()) {
|
||||
skin = reset ? mojangAPI.getSkinWithoutCaching(uuid.get()) : mojangAPI.getSkin(uuid.get());
|
||||
if (skin.isPresent()) {
|
||||
final MojangSkin skinResult = skin.get();
|
||||
final Multimap<String, WrappedSignedProperty> properties = gameProfile.getProperties();
|
||||
properties.get("textures").clear();
|
||||
properties.put("textures", new WrappedSignedProperty("textures", skinResult.value(), skinResult.signature()));
|
||||
} else {
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_SKIN);
|
||||
}
|
||||
} else {
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
|
||||
}
|
||||
return ActionResult.ok();
|
||||
} catch (ExecutionException e) {
|
||||
return ActionResult.error(LanguageKey.Error.CACHE);
|
||||
} catch (IOException e) {
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
|
||||
} catch (InterruptedException e) {
|
||||
return ActionResult.error("Unknown error");
|
||||
}
|
||||
}
|
||||
return ActionResult.ok();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendEntityMetadataUpdate() {
|
||||
final WrappedDataWatcher entityWatcher = WrappedDataWatcher.getEntityWatcher(player);
|
||||
entityWatcher.setObject(17, (byte) 0x7f, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPlayerRespawn() {
|
||||
final World world = player.getWorld();
|
||||
|
||||
final WrapperPlayServerRespawn respawn = new WrapperPlayServerRespawn();
|
||||
respawn.setDimension(world);
|
||||
respawn.setSeed(world.getSeed());
|
||||
respawn.setGameMode(player.getGameMode());
|
||||
respawn.setPreviousGameMode(player.getGameMode());
|
||||
respawn.setCopyMetadata(true);
|
||||
respawn.sendPacket(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTabListUpdate(String displayName) {
|
||||
if (gameProfile == null) {
|
||||
Nicko.getInstance().getLogger().warning("Hello. I sincerely hope you're doing great out there.");
|
||||
Nicko.getInstance().getLogger().warning("If you see this message, I've failed at my task and I'm a terrible programmer.");
|
||||
Nicko.getInstance().getLogger().warning("Report this issue on https://git.ineanto.xyz/ineanto/nicko, thank you!");
|
||||
return;
|
||||
}
|
||||
|
||||
final WrapperPlayerServerPlayerInfo add = new WrapperPlayerServerPlayerInfo();
|
||||
final WrapperPlayerServerPlayerInfoRemove remove = new WrapperPlayerServerPlayerInfoRemove();
|
||||
final EnumSet<EnumWrappers.PlayerInfoAction> actions = EnumSet.of(
|
||||
EnumWrappers.PlayerInfoAction.ADD_PLAYER,
|
||||
EnumWrappers.PlayerInfoAction.INITIALIZE_CHAT,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_LISTED,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_DISPLAY_NAME,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_LATENCY);
|
||||
remove.setUUIDs(List.of(player.getUniqueId()));
|
||||
remove.broadcastPacket();
|
||||
add.setActions(actions);
|
||||
|
||||
add.setData(List.of(new PlayerInfoData(
|
||||
player.getUniqueId(),
|
||||
player.getPing(),
|
||||
true,
|
||||
EnumWrappers.NativeGameMode.fromBukkit(player.getGameMode()),
|
||||
gameProfile,
|
||||
WrappedChatComponent.fromText(displayName),
|
||||
WrappedRemoteChatSessionData.fromPlayer(player)
|
||||
)));
|
||||
|
||||
add.broadcastPacket();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package xyz.ineanto.nicko.debug;
|
||||
package xyz.ineanto.nicko.packet.debug;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.ListeningWhitelist;
|
|
@ -16,7 +16,7 @@
|
|||
* 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 xyz.ineanto.nicko.wrapper;
|
||||
package xyz.ineanto.nicko.packet.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
|
@ -1,4 +1,4 @@
|
|||
package xyz.ineanto.nicko.wrapper;
|
||||
package xyz.ineanto.nicko.packet.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
|
@ -1,4 +1,4 @@
|
|||
package xyz.ineanto.nicko.wrapper;
|
||||
package xyz.ineanto.nicko.packet.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.InternalStructure;
|
||||
|
@ -56,11 +56,20 @@ public class WrapperPlayServerRespawn extends AbstractPacket {
|
|||
} else {
|
||||
// 1.20.5 to 1.21.1
|
||||
|
||||
/*
|
||||
Honestly, I've tried everything to make this work.
|
||||
Fields inside the CommonPlayerSpawnInfo are Record Components and are
|
||||
marked final.
|
||||
|
||||
This would work with some trickery involved, but here's the
|
||||
caveat: Record Components/Fields and are immutable by DESIGN.
|
||||
So... here we are now, stopped right in my track by Java's language design and Mojang themselves.
|
||||
*/
|
||||
|
||||
try {
|
||||
final Object spawnInfoStructureHandle = spawnInfoStructure.getHandle();
|
||||
final RecordComponent[] components = spawnInfoStructureHandle.getClass().getRecordComponents();
|
||||
|
||||
// Doesn't work!
|
||||
final Field levelKeyField = spawnInfoStructureHandle.getClass().getDeclaredField(components[1].getAccessor().getName());
|
||||
levelKeyField.setAccessible(true);
|
||||
levelKeyField.set(spawnInfoStructureHandle, BukkitConverters.getWorldKeyConverter().getGeneric(Bukkit.getWorld("world")));
|
|
@ -1,4 +1,4 @@
|
|||
package xyz.ineanto.nicko.wrapper;
|
||||
package xyz.ineanto.nicko.packet.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
|
@ -1,4 +1,4 @@
|
|||
package xyz.ineanto.nicko.wrapper;
|
||||
package xyz.ineanto.nicko.packet.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
|
@ -1,4 +1,4 @@
|
|||
package xyz.ineanto.nicko.wrapper;
|
||||
package xyz.ineanto.nicko.packet.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
|
@ -4,14 +4,19 @@ import org.bukkit.entity.Player;
|
|||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.language.Language;
|
||||
import xyz.ineanto.nicko.storage.PlayerDataStore;
|
||||
import xyz.ineanto.nicko.storage.name.PlayerNameStore;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class NickoProfile implements Cloneable {
|
||||
public static final PlayerDataStore dataStore = Nicko.getInstance().getDataStore();
|
||||
public static final NickoProfile EMPTY_PROFILE = new NickoProfile(null, null, Language.ENGLISH, true);
|
||||
|
||||
private static final Nicko instance = Nicko.getInstance();
|
||||
private static final PlayerDataStore dataStore = instance.getDataStore();
|
||||
|
||||
private final PlayerNameStore nameStore = instance.getNameStore();
|
||||
|
||||
private String name;
|
||||
private String skin;
|
||||
private Language language;
|
||||
|
|
Loading…
Reference in a new issue