Skip to content

Instantly share code, notes, and snippets.

@DarkSeraphim
Last active November 5, 2015 16:07
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save DarkSeraphim/8ae6f42b3f88a9ea006f to your computer and use it in GitHub Desktop.
Save DarkSeraphim/8ae6f42b3f88a9ea006f to your computer and use it in GitHub Desktop.
Using the ActionBar in 1.8 MC clients (currently written for the Spigot protocol hack). Explanation here: http://www.spigotmc.org/threads/news-bar-over-the-hotbar.30394/#post-345167
package net.darkseraphim.actionbar;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import net.minecraft.server.v1_7_R4.EnumProtocol;
import net.minecraft.server.v1_7_R4.NetworkManager;
import net.minecraft.server.v1_7_R4.Packet;
import net.minecraft.server.v1_7_R4.PacketPlayOutChat;
import net.minecraft.util.com.google.common.collect.BiMap;
import net.minecraft.util.io.netty.channel.Channel;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
public class ActionBarExample extends JavaPlugin
{
private BiMap map;
private ProxyHashBiMap<Integer, Class> special;
@Override
public void onEnable()
{
overrideI();
injectCustomPacket(PacketPlayOutChat.class, PacketPlayOutActionBar.class);
refreshPlayers(true);
}
@Override
public void onDisable()
{
refreshPlayers(false);
ejectCustomPacket(PacketPlayOutChat.class, PacketPlayOutActionBar.class);
returnI();
}
private void overrideI()
{
try
{
Field field = EnumProtocol.class.getDeclaredField("i");
field.setAccessible(true);
this.map = (BiMap) field.get(EnumProtocol.PLAY);
special = new ProxyHashBiMap<Integer, Class>(map);
set(field, EnumProtocol.PLAY, special);
}
catch(Exception ex)
{
ex.printStackTrace();
throw new ExceptionInInitializerError("Nope, not allowed.");
}
}
private void set(Field f, Object o, Object v) throws NoSuchFieldException, IllegalAccessException
{
boolean accessible = f.isAccessible();
f.setAccessible(true);
if ((f.getModifiers() & Modifier.FINAL) != 0)
{
set(Field.class.getDeclaredField("modifiers"), f, f.getModifiers() & ~Modifier.FINAL);
}
f.set(o, v);
}
public <T extends Packet> void injectCustomPacket(Class<T> mc, Class<? extends T> custom)
{
special.inverse().injectSpecial(PacketPlayOutActionBar.class, PacketPlayOutChat.class);
try
{
Field field = EnumProtocol.class.getDeclaredField("f");
field.setAccessible(true);
((Map)field.get(null)).put(custom, EnumProtocol.PLAY);
}
catch (NoSuchFieldException ex)
{
ex.printStackTrace();
}
catch (IllegalAccessException ex)
{
ex.printStackTrace();
}
}
public <T extends Packet> void ejectCustomPacket(Class<? extends T> custom)
{
special.inverse().ejectSpecial(custom);
//Validate.notNull(id, "You can only eject existing packets");
try
{
Field field = EnumProtocol.class.getDeclaredField("f");
field.setAccessible(true);
((Map)field.get(null)).remove(custom);
}
catch (NoSuchFieldException ex)
{
ex.printStackTrace();
}
catch (IllegalAccessException ex)
{
ex.printStackTrace();
}
}
private void returnI()
{
try
{
Field field = EnumProtocol.class.getDeclaredField("i");
field.setAccessible(true);
set(field, EnumProtocol.PLAY, this.map);
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
private void refreshPlayers(boolean enable)
{
try
{
Field channelField = NetworkManager.class.getDeclaredField("m");
channelField.setAccessible(true);
Channel channel;
for (Player player : Bukkit.getOnlinePlayers())
{
channel = (Channel) channelField.get(((CraftPlayer) player).getHandle().playerConnection.networkManager);
channel.attr(NetworkManager.f).set(enable ? this.special : this.map);
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
package net.darkseraphim.testmodule;
import java.io.IOException;
import com.comphenix.protocol.events.PacketEvent;
import net.minecraft.server.v1_7_R4.*;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_7_R4.util.CraftChatMessage;
import org.bukkit.entity.Player;
/**
* @author DarkSeraphim
*/
public class PacketPlayOutActionBar extends PacketPlayOutChat
{
public String json;
/**
* Creates an action bar message
* @param message The message to display
*/
public PacketPlayOutActionBar(String message)
{
super();
this.json = "{\"text\":\""+message+"\"}";
}
/**
* When using md_5's chat API
* @param components Components of which the chat consists of
* @deprecated Doesn't support colours and formatting
*/
@Deprecated
public PacketPlayOutActionBar(BaseComponent...components)
{
super();
this.json = ComponentSerializer.toString(components);
}
public void a(PacketDataSerializer packetdataserializer) throws IOException
{
this.json = ChatSerializer.a(ChatSerializer.a(packetdataserializer.c(32767)));
}
public void b(PacketDataSerializer packetdataserializer) throws IOException
{
packetdataserializer.a(this.json);
packetdataserializer.writeByte(0x2);
}
public void a(PacketPlayOutListener packetplayoutlistener)
{
packetplayoutlistener.a(this);
}
public String b()
{
return String.format("actionbar='%s'", new Object[] { this.json });
}
public void handle(PacketListener packetlistener)
{
a((PacketPlayOutListener)packetlistener);
}
// Added some helper methods
public void send(Player player)
{
if(((CraftPlayer)player).getHandle().playerConnection.networkManager.getVersion() < 47)
return;
((CraftPlayer)player).getHandle().playerConnection.sendPacket(this);
}
public void broadcast()
{
for(Player player : Bukkit.getOnlinePlayers())
{
send(player);
}
}
}
package net.darkseraphim.testmodule;
import net.minecraft.server.v1_7_R4.PacketPlayOutChat;
import net.minecraft.util.com.google.common.collect.BiMap;
import net.minecraft.util.com.google.common.collect.HashBiMap;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author DarkSeraphim
*/
public class ProxyHashBiMap<K, V> implements BiMap<K, V>
{
private BiMap<K, V> delegate;
private ProxyHashBiMap<V, K> inverse;
// This will proxy
protected Map<K, K> proxy = new HashMap<K, K>(1);
public ProxyHashBiMap(BiMap<K, V> map)
{
this.delegate = map;
this.inverse = new Inverse(this, map.inverse(), map);
}
private ProxyHashBiMap(BiMap<K, V> map, BiMap<V, K> inversed)
{
this.delegate = map;
this.inverse = null;
}
@Override
public int size()
{
return this.delegate.size();
}
@Override
public boolean isEmpty()
{
return this.delegate.isEmpty();
}
@Override
public boolean containsKey(Object key)
{
return this.delegate.containsKey(key);
}
@Override
public boolean containsValue(Object value)
{
return this.delegate.containsValue(value);
}
@Override
public V get(Object key)
{
Object okey = null;
if((okey = this.proxy.get(key)) != null)
key = okey;
return this.delegate.get(key);
}
@Override
public V put(@Nullable K k, @Nullable V v)
{
return this.delegate.put(k, v);
}
@Override
public V remove(Object key)
{
return this.delegate.remove(key);
}
@Override
public V forcePut(@Nullable K k, @Nullable V v)
{
return this.delegate.forcePut(k, v);
}
@Override
public void putAll(Map<? extends K, ? extends V> map)
{
this.delegate.clear();
}
@Override
public void clear()
{
this.delegate.clear();
}
@Override
public Set<K> keySet()
{
return this.delegate.keySet();
}
@Override
public Set<V> values()
{
return this.delegate.values();
}
@Override
public Set<Entry<K, V>> entrySet()
{
return this.delegate.entrySet();
}
@Override
public ProxyHashBiMap<V, K> inverse()
{
return this.inverse;
}
public void injectSpecial(K key, K okey)
{
this.proxy.put(key, okey);
}
public void ejectSpecial(K key)
{
this.proxy.remove(key);
}
private class Inverse<V, K> extends ProxyHashBiMap<V, K>
{
private ProxyHashBiMap original;
private Inverse(ProxyHashBiMap original, BiMap<V, K> forward, BiMap<K, V> backward)
{
super(forward, backward);
this.original = original;
}
/**
* Prevent creation of more Maps, just return the original
* @return the original BiMap
*/
@Override
public ProxyHashBiMap<K, V> inverse()
{
return this.original;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment