Skip to content

Commit

Permalink
event player receives tablist update
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmonkey4eva committed Apr 6, 2022
1 parent 83ea30d commit 59f3c35
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 0 deletions.
Expand Up @@ -196,6 +196,7 @@ public static void registerMainEvents() {
ScriptEvent.registerScriptEvent(PlayerReceivesActionbarScriptEvent.class);
ScriptEvent.registerScriptEvent(PlayerReceivesCommandsScriptEvent.class);
ScriptEvent.registerScriptEvent(PlayerReceivesMessageScriptEvent.class);
ScriptEvent.registerScriptEvent(PlayerReceivesTablistUpdateScriptEvent.class);
ScriptEvent.registerScriptEvent(PlayerRespawnsScriptEvent.class);
ScriptEvent.registerScriptEvent(PlayerRightClicksEntityScriptEvent.class);
ScriptEvent.registerScriptEvent(PlayerRiptideScriptEvent.class);
Expand Down
@@ -0,0 +1,199 @@
package com.denizenscript.denizen.events.player;

import com.denizenscript.denizen.events.BukkitScriptEvent;
import com.denizenscript.denizen.utilities.debugging.Debug;
import com.denizenscript.denizen.utilities.implementation.BukkitScriptEntryData;
import com.denizenscript.denizen.utilities.packets.NetworkInterceptHelper;
import com.denizenscript.denizencore.objects.ObjectTag;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.scripts.ScriptEntryData;
import com.denizenscript.denizencore.utilities.CoreConfiguration;
import com.denizenscript.denizencore.utilities.CoreUtilities;
import org.bukkit.entity.Player;

import java.util.UUID;

public class PlayerReceivesTablistUpdateScriptEvent extends BukkitScriptEvent {

// <--[event]
// @Events
// player receives tablist update
//
// @Switch mode:add/update/remove to only trigger if the tablist update is the specified mode.
//
// @Group Player
//
// @Triggers when a player receives a tablist update.
//
// @Location true
// @Cancellable true
//
// @Context
// <context.mode> returns the update mode: 'add', 'remove', 'update_gamemode', 'update_latency', or 'update_display'.
// <context.uuid> returns the packet's associated UUID.
// <context.name> returns the packet's associated name (if any).
// <context.display> returns the packet's associated display name (if any).
// <context.latency> returns the packet's associated latency (if any).
// <context.gamemode> returns the packet's associated gamemode (if any).
// <context.skin_blob> returns the packet's associated skin blob (if any).
//
// @Determine
// "LATENCY:" + ElementTag(Number) to change the latency.
// "NAME:" + ElementTag to change the name.
// "DISPLAY:" + ElementTag to change the display name. 'name', 'display' and 'cancelled' determinations require 'Allow restricted actions' in Denizen/config.yml
// "GAMEMODE:" + ElementTag to change the gamemode.
// "SKIN_BLOB:" + ElementTag to change the skin blob.
//
// @Player Always.
//
// -->

public PlayerReceivesTablistUpdateScriptEvent() {
instance = this;
registerCouldMatcher("player receives tablist update");
registerSwitches("mode");
}

public static PlayerReceivesTablistUpdateScriptEvent instance;

public static class TabPacketData {

public TabPacketData(String mode, UUID id, String name, String display, String gamemode, String texture, String signature, int latency) {
this.mode = mode;
this.id = id;
this.name = name;
this.display = display;
this.gamemode = gamemode;
this.texture = texture;
this.signature = signature;
this.latency = latency;
}

public UUID id;

public String mode, name, display, gamemode, texture, signature;

public int latency;

public boolean cancelled = false;

public boolean modified = false;
}

public Player player;

public TabPacketData data;

public static boolean enabled = false;

@Override
public void init() {
NetworkInterceptHelper.enable();
enabled = true;
}

@Override
public void destroy() {
enabled = false;
}

@Override
public void cancellationChanged() {
if (cancelled && !CoreConfiguration.allowRestrictedActions) {
Debug.echoError("Cannot use 'receives tablist update' event to cancel a tablist packet: 'Allow restricted actions' is disabled in Denizen config.yml.");
return;
}
data.cancelled = cancelled;
data.modified = true;
}

@Override
public boolean matches(ScriptPath path) {
if (!runInCheck(path, player.getLocation())) {
return false;
}
if (!runGenericSwitchCheck(path, "mode", data.mode)) {
return false;
}
return super.matches(path);
}

@Override
public String getName() {
return "PlayerReceivesTablistUpdate";
}

@Override
public boolean applyDetermination(ScriptPath path, ObjectTag determinationObj) {
String determination = determinationObj.toString();
String determinationLow = CoreUtilities.toLowerCase(determination);
if (determinationLow.contains(":")) {
if (determinationLow.startsWith("latency:")) {
data.modified = true;
data.latency = Integer.parseInt(determination.substring("latency:".length()));
return true;
}
else if (determinationLow.startsWith("name:")) {
if (!CoreConfiguration.allowRestrictedActions) {
Debug.echoError("Cannot use 'receives tablist update' event to edit a display name: 'Allow restricted actions' is disabled in Denizen config.yml.");
return true;
}
data.modified = true;
data.name = determination.substring("name:".length());
return true;
}
else if (determinationLow.startsWith("display:")) {
if (!CoreConfiguration.allowRestrictedActions) {
Debug.echoError("Cannot use 'receives tablist update' event to edit a display name: 'Allow restricted actions' is disabled in Denizen config.yml.");
return true;
}
data.modified = true;
data.name = determination.substring("display:".length());
return true;
}
else if (determinationLow.startsWith("gamemode:")) {
data.modified = true;
data.gamemode = determination.substring("gamemode:".length());
return true;
}
else if (determinationLow.startsWith("skin_blob:")) {
String blob = determination.substring("skin_blob:".length());
int semicolon = blob.indexOf(';');
if (semicolon == -1) {
Debug.echoError("Invalid skin blob!");
return true;
}
data.modified = true;
data.texture = blob.substring(0, semicolon);
data.signature = blob.substring(semicolon + 1);
return true;
}
}
return super.applyDetermination(path, determinationObj);
}

@Override
public ScriptEntryData getScriptEntryData() {
return new BukkitScriptEntryData(player);
}

@Override
public ObjectTag getContext(String name) {
switch (name) {
case "name": return new ElementTag(data.name);
case "uuid": return new ElementTag(data.id.toString());
case "mode": return new ElementTag(data.mode);
case "display": return new ElementTag(data.display);
case "gamemode": return new ElementTag(data.gamemode);
case "skin_blob": return new ElementTag(data.texture + ";" + data.signature);
case "latency": return new ElementTag(data.latency);
}
return super.getContext(name);
}

public static void fire(Player player, TabPacketData data) {
instance.player = player;
instance.data = data;
instance.fire();
}
}
Expand Up @@ -63,6 +63,10 @@ public TablistCommand() {
// Use to add a new empty entry to the player's tab list to fill space.
// - tablist add name:<empty> display:<empty> gamemode:spectator
//
// @Usage
// Use to add a custom tab completion in the in-game chat, by adding an empty entry to the bottom of the tab list.
// - tablist add name:my_tab_complete display:<empty> gamemode:spectator
//
// -->

public enum Mode { ADD, REMOVE, UPDATE }
Expand Down
Expand Up @@ -2,6 +2,7 @@

import com.denizenscript.denizen.events.player.PlayerHearsSoundScriptEvent;
import com.denizenscript.denizen.events.player.PlayerReceivesActionbarScriptEvent;
import com.denizenscript.denizen.events.player.PlayerReceivesTablistUpdateScriptEvent;
import com.denizenscript.denizen.nms.abstracts.BlockLight;
import com.denizenscript.denizen.nms.v1_18.Handler;
import com.denizenscript.denizen.nms.v1_18.ReflectionMappingsInfo;
Expand All @@ -27,6 +28,8 @@
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.utilities.CoreConfiguration;
import com.denizenscript.denizencore.utilities.CoreUtilities;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.datafixers.util.Pair;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
Expand Down Expand Up @@ -58,6 +61,7 @@
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.Bukkit;
import org.bukkit.Location;
Expand Down Expand Up @@ -294,6 +298,7 @@ public void send(Packet<?> packet, GenericFutureListener<? extends Future<? supe
|| processMirrorForPacket(packet)
|| processParticlesForPacket(packet)
|| processSoundPacket(packet)
|| processTablistPacket(packet, genericfuturelistener)
|| processActionbarPacket(packet, genericfuturelistener)
|| processDisguiseForPacket(packet, genericfuturelistener)
|| processMetadataChangesForPacket(packet, genericfuturelistener)
Expand All @@ -308,6 +313,80 @@ public void send(Packet<?> packet, GenericFutureListener<? extends Future<? supe
oldManager.send(packet, genericfuturelistener);
}

public boolean processTablistPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
if (!PlayerReceivesTablistUpdateScriptEvent.enabled) {
return false;
}
if (packet instanceof ClientboundPlayerInfoPacket) {
ClientboundPlayerInfoPacket infoPacket = (ClientboundPlayerInfoPacket) packet;
String mode;
switch (infoPacket.getAction()) {
case ADD_PLAYER:
mode = "add";
break;
case REMOVE_PLAYER:
mode = "remove";
break;
case UPDATE_LATENCY:
mode = "update_latency";
break;
case UPDATE_GAME_MODE:
mode = "update_gamemode";
break;
case UPDATE_DISPLAY_NAME:
mode = "update_display";
break;
default:
return false;
}
boolean isOverriding = false;
for (ClientboundPlayerInfoPacket.PlayerUpdate update : infoPacket.getEntries()) {
GameProfile profile = update.getProfile();
String texture = null, signature = null;
if (profile.getProperties().containsKey("textures")) {
Property property = profile.getProperties().get("textures").stream().findFirst().get();
texture = property.getValue();
signature = property.getSignature();
}
PlayerReceivesTablistUpdateScriptEvent.TabPacketData data = new PlayerReceivesTablistUpdateScriptEvent.TabPacketData(mode, profile.getId(), profile.getName(),
update.getDisplayName() == null ? null : FormattedTextHelper.stringify(Handler.componentToSpigot(update.getDisplayName()), ChatColor.WHITE), update.getGameMode().name(), texture, signature, update.getLatency());
PlayerReceivesTablistUpdateScriptEvent.fire(player.getBukkitEntity(), data);
if (data.modified) {
if (!isOverriding) {
isOverriding = true;
ClientboundPlayerInfoPacket priorsPacket = new ClientboundPlayerInfoPacket(infoPacket.getAction());
for (ClientboundPlayerInfoPacket.PlayerUpdate priorUpdate : infoPacket.getEntries()) {
if (priorUpdate == update) {
break;
}
priorsPacket.getEntries().add(priorUpdate);
}
if (!priorsPacket.getEntries().isEmpty()) {
oldManager.send(priorsPacket, genericfuturelistener);
}
}
if (!data.cancelled) {
ClientboundPlayerInfoPacket newPacket = new ClientboundPlayerInfoPacket(infoPacket.getAction());
GameProfile newProfile = new GameProfile(data.id, data.name);
if (data.texture != null) {
newProfile.getProperties().put("textures", new Property("textures", data.texture, data.signature));
}
newPacket.getEntries().add(new ClientboundPlayerInfoPacket.PlayerUpdate(newProfile, data.latency, GameType.byName(CoreUtilities.toLowerCase(data.gamemode)),
data.display == null ? null : Handler.componentToNMS(FormattedTextHelper.parse(data.display, ChatColor.WHITE))));
oldManager.send(newPacket, genericfuturelistener);
}
}
else if (isOverriding) {
ClientboundPlayerInfoPacket newPacket = new ClientboundPlayerInfoPacket(infoPacket.getAction());
newPacket.getEntries().add(update);
oldManager.send(newPacket, genericfuturelistener);
}
}
return isOverriding;
}
return false;
}

public boolean processActionbarPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
if (!PlayerReceivesActionbarScriptEvent.instance.loaded) {
return false;
Expand Down

0 comments on commit 59f3c35

Please sign in to comment.