Last active
November 5, 2015 16:07
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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