Somewhat experimental: Add time of day on server to server list GUI

This commit is contained in:
tterrag committed 2015-11-29 23:40:51 -05:00
1 parent 6f772aa1d9
commit 2399aba5ea
3 files changed
+469

No files matched your search

@@ -0,0 +1,70 @@
package com.enderio.core.client.gui.serverlist;
import java.io.IOException;
import java.lang.reflect.Field;
import net.minecraft.network.INetHandler;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.NetHandlerStatusServer;
import com.enderio.core.client.gui.serverlist.TimeServerPinger.NetHandlerTime;
import com.google.common.base.Throwables;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.network.ByteBufUtils;
import cpw.mods.fml.relauncher.ReflectionHelper;
public class PacketServerTime extends Packet {
private long time;
private String serverIP;
public PacketServerTime() {
this("");
}
public PacketServerTime(String serverIP) {
this(0, serverIP);
}
private PacketServerTime(long time, String serverIP) {
this.time = time;
this.serverIP = serverIP;
}
@Override
public void readPacketData(PacketBuffer buf) throws IOException {
this.time = buf.readLong();
this.serverIP = ByteBufUtils.readUTF8String(buf);
}
@Override
public void writePacketData(PacketBuffer buf) throws IOException {
buf.writeLong(getTime());
ByteBufUtils.writeUTF8String(buf, serverIP);
}
private static final Field _networkManager = ReflectionHelper.findField(NetHandlerStatusServer.class, "field_147313_b", "networkManager");
@Override
public void processPacket(INetHandler net) {
if (FMLCommonHandler.instance().getEffectiveSide().isServer()) {
try {
MinecraftServer serv = MinecraftServer.getServer();
NetworkManager resp = (NetworkManager) _networkManager.get(net);
resp.scheduleOutboundPacket(new PacketServerTime(serv.getEntityWorld().getWorldTime(), serverIP));
} catch (Exception e) {
Throwables.propagate(e);
}
} else {
((NetHandlerTime) net).handleTime(this);
}
}
public long getTime() {
return time;
}
}
@@ -0,0 +1,271 @@
package com.enderio.core.client.gui.serverlist;
import gnu.trove.map.hash.TObjectLongHashMap;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiListExtended.IGuiListEntry;
import net.minecraft.client.gui.GuiMultiplayer;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ServerListEntryNormal;
import net.minecraft.client.gui.ServerSelectionList;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.texture.TextureClock;
import net.minecraft.client.renderer.texture.TextureUtil;
import net.minecraft.init.Items;
import net.minecraft.network.EnumConnectionState;
import net.minecraft.world.WorldProvider;
import net.minecraftforge.client.event.GuiScreenEvent.DrawScreenEvent;
import net.minecraftforge.client.event.GuiScreenEvent.InitGuiEvent;
import net.minecraftforge.common.DimensionManager;
import org.lwjgl.opengl.GL11;
import com.enderio.core.client.handlers.ClientHandler;
import com.enderio.core.client.render.RenderUtil;
import com.enderio.core.common.Handlers.Handler;
import com.enderio.core.common.Handlers.Handler.HandlerSide;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent;
import cpw.mods.fml.relauncher.ReflectionHelper;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SuppressWarnings("unchecked")
@Handler(side = HandlerSide.COMMON)
public class ServerListHandler {
private static class TextureClockCustom extends TextureClock {
private static final Field field_94239_h = ReflectionHelper.findField(TextureClock.class, "field_94239_h");
private static final Field field_94240_i = ReflectionHelper.findField(TextureClock.class, "field_94240_i");
private static final Field _framesTextureData = ReflectionHelper.findField(TextureAtlasSprite.class, "field_110976_a", "framesTextureData");
private static final WorldProvider provider = DimensionManager.createProviderFor(0);
private String serverIP;
public TextureClockCustom(String serverIP) throws IllegalArgumentException, IllegalAccessException {
super("clock-custom");
this.serverIP = serverIP;
this.framesTextureData = (List<?>) _framesTextureData.get(Items.clock.getIconFromDamage(0));
}
@Override
public void updateAnimation() {
if (!this.framesTextureData.isEmpty()) {
try {
float f = provider.calculateCelestialAngle(timeCache.get(serverIP), 1.0F);
double angle = (double) f;
if (!provider.isSurfaceWorld()) {
angle = Math.random();
}
double d1;
for (d1 = angle - field_94239_h.getDouble(this); d1 < -0.5D; ++d1) {
;
}
while (d1 >= 0.5D) {
--d1;
}
if (d1 < -1.0D) {
d1 = -1.0D;
}
if (d1 > 1.0D) {
d1 = 1.0D;
}
field_94240_i.setDouble(this, field_94240_i.getDouble(this) + d1 * 0.1D);
field_94240_i.setDouble(this, field_94240_i.getDouble(this) * 0.8D);
field_94239_h.setDouble(this, field_94239_h.getDouble(this) + field_94240_i.getDouble(this));
int i;
for (i = (int) ((field_94239_h.getDouble(this) + 1.0D) * (double) this.framesTextureData.size()) % this.framesTextureData.size(); i < 0; i = (i + this.framesTextureData
.size()) % this.framesTextureData.size()) {
;
}
if (i != this.frameCounter) {
this.frameCounter = i;
TextureUtil.uploadTextureMipmap((int[][]) this.framesTextureData.get(this.frameCounter), this.width, this.height, this.originX, this.originY,
false, false);
// For some reason this can spam a GL error, it is harmless from what I can see, so we'll just throw it away
GL11.glGetError();
}
} catch (Exception e) {
Throwables.propagate(e);
}
}
}
}
static {
EnumConnectionState.STATUS.func_150753_a().put(42, PacketServerTime.class);
EnumConnectionState.STATUS.func_150755_b().put(42, PacketServerTime.class);
try {
((Map<Class<?>, EnumConnectionState>) ReflectionHelper.findField(EnumConnectionState.class, "field_150761_f", "STATES_BY_CLASS").get(null)).put(
PacketServerTime.class, EnumConnectionState.STATUS);
} catch (Exception e) {
Throwables.propagate(e);
}
}
private static Field _serverList;
private static Method _getSize;
private static Method _drawHoveringText;
private static TimeServerPinger pinger;
private static Set<String> capturedIps = Sets.newHashSet();
@SideOnly(Side.CLIENT)
private static GuiMultiplayer openGui;
@SideOnly(Side.CLIENT)
private static ServerSelectionList serverList;
static TObjectLongHashMap<String> timeCache = new TObjectLongHashMap<String>();
private static Map<String, TextureClockCustom> textures = Maps.newHashMap();
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onDrawGui(DrawScreenEvent.Post event) {
if (openGui != null) {
if (serverList != null) {
int x = serverList.left + (serverList.width / 2) + (serverList.getListWidth() / 2) - 32;
int y = serverList.top + 13 - serverList.getAmountScrolled();
// System.out.println(timeCache);
try {
int size = (Integer) _getSize.invoke(serverList);
for (int i = 0; i < size; i++) {
IGuiListEntry entry = serverList.getListEntry(i);
if (entry instanceof ServerListEntryNormal) {
ServerData data = ((ServerListEntryNormal) entry).func_148296_a();
RenderUtil.bindItemTexture();
TextureAtlasSprite sprite = textures.get(data.serverIP);
if (sprite != null) {
Tessellator tess = Tessellator.instance;
GL11.glPushMatrix();
GL11.glColor4f(1, 1, 1, 1);
GL11.glTranslated(0, 0, -500);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glDepthMask(true);
tess.startDrawingQuads();
tess.addVertexWithUV(x, y, 0, sprite.getMinU(), sprite.getMinV());
tess.addVertexWithUV(x, y + 16, 0, sprite.getMinU(), sprite.getMaxV());
tess.addVertexWithUV(x + 16, y + 16, 0, sprite.getMaxU(), sprite.getMaxV());
tess.addVertexWithUV(x + 16, y, 0, sprite.getMaxU(), sprite.getMinV());
tess.draw();
GL11.glPopMatrix();
if (event.mouseX >= x && event.mouseX <= x + 16 && event.mouseY >= y && event.mouseY <= y + 16) {
long time = timeCache.get(data.serverIP);
long hour = (time / 1000 + 6) % 24;
long minute = (time % 1000) * 60 / 1000;
String ampm = "AM";
if (hour >= 12) {
hour -= 12;
ampm = "PM";
}
if (hour == 0) {
hour += 12;
}
String tt = String.format(Locale.ENGLISH, "%02d:%02d %s", hour, minute, ampm);
_drawHoveringText.invoke(event.gui, Lists.newArrayList(tt), event.mouseX, event.mouseY, Minecraft.getMinecraft().fontRenderer);
}
}
y += serverList.getSlotHeight();
}
}
} catch (Exception e) {
Throwables.propagate(e);
}
}
if (ClientHandler.getTicksElapsed() % 100 == 0) {
openConnections(openGui);
}
}
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onGuiOpen(InitGuiEvent.Post event) {
if (_serverList == null) {
_serverList = ReflectionHelper.findField(GuiMultiplayer.class, "field_146803_h", "serverListSelector");
_getSize = ReflectionHelper.findMethod(ServerSelectionList.class, null, new String[] { "func_148127_b", "getSize" });
_drawHoveringText = ReflectionHelper.findMethod(GuiScreen.class, null, new String[] { "func_146283_a", "drawHoveringText" }, List.class, int.class, int.class, FontRenderer.class);
}
if (pinger != null) {
pinger.close();
}
if (event.gui instanceof GuiMultiplayer) {
openGui = (GuiMultiplayer) event.gui;
pinger = new TimeServerPinger();
capturedIps.clear();
openConnections(openGui);
}
}
private void openConnections(GuiMultiplayer gui) {
try {
serverList = (ServerSelectionList) _serverList.get(gui);
int size = (Integer) _getSize.invoke(serverList);
for (int i = 0; i < size; i++) {
IGuiListEntry entry = serverList.getListEntry(i);
if (entry instanceof ServerListEntryNormal) {
ServerData data = ((ServerListEntryNormal) entry).func_148296_a();
if (!capturedIps.contains(data.serverIP)) {
capturedIps.add(data.serverIP);
pinger.addServer(data);
if (!textures.containsKey(data.serverIP)) {
TextureClockCustom sprite = new TextureClockCustom(data.serverIP);
sprite.copyFrom((TextureAtlasSprite) Items.clock.getIconFromDamage(0));
textures.put(data.serverIP, sprite);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onClientTick(ClientTickEvent event) {
if (Minecraft.getMinecraft().currentScreen != openGui) {
openGui = null;
} else if (openGui != null) {
pinger.update(ClientHandler.getTicksElapsed() % 20 == 0);
for (TextureClockCustom tex : textures.values()) {
tex.updateAnimation();
}
}
if (openGui == null && pinger != null) {
pinger.close();
pinger = null;
}
}
}
@@ -0,0 +1,128 @@
package com.enderio.core.client.gui.serverlist;
import java.io.Closeable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import net.minecraft.client.multiplayer.ServerAddress;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.network.EnumConnectionState;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.handshake.client.C00Handshake;
import net.minecraft.network.status.INetHandlerStatusClient;
import net.minecraft.network.status.server.S00PacketServerInfo;
import net.minecraft.network.status.server.S01PacketPong;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.IChatComponent;
import org.apache.logging.log4j.Logger;
import com.enderio.core.EnderCore;
import com.google.common.collect.Maps;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class TimeServerPinger implements Closeable {
public static class NetHandlerTime implements INetHandlerStatusClient {
private NetworkManager manager;
private ServerData data;
private boolean done;
public NetHandlerTime(NetworkManager manager, ServerData data) {
this.manager = manager;
this.data = data;
}
public void onDisconnect(IChatComponent p_147231_1_) {
if (!done) {
logger.error("Can\'t ping " + data.serverIP + " for time info" + ": " + p_147231_1_.getUnformattedText());
}
}
public void handleTime(PacketServerTime packet) {
ServerListHandler.timeCache.put(data.serverIP, packet.getTime());
done = true;
manager.closeChannel(new ChatComponentText("Finished"));
}
@Override
public void handlePong(S01PacketPong packet) {
// NO-OP
}
@Override
public void handleServerInfo(S00PacketServerInfo p_147397_1_) {
// NO-OP
}
public void onConnectionStateTransition(EnumConnectionState p_147232_1_, EnumConnectionState p_147232_2_) {
if (p_147232_2_ != EnumConnectionState.STATUS) {
throw new UnsupportedOperationException("Unexpected change in protocol to " + p_147232_2_);
}
}
public void onNetworkTick() {
}
}
private static final Logger logger = EnderCore.logger;
private final Map<NetworkManager, ServerData> managers = Collections.synchronizedMap(Maps.<NetworkManager, ServerData> newHashMap());
public void addServer(final ServerData data) throws UnknownHostException {
ServerAddress serveraddress = ServerAddress.func_78860_a(data.serverIP);
final NetworkManager networkmanager = NetworkManager.provideLanClient(InetAddress.getByName(serveraddress.getIP()), serveraddress.getPort());
this.managers.put(networkmanager, data);
networkmanager.setNetHandler(new NetHandlerTime(networkmanager, data));
try {
networkmanager.scheduleOutboundPacket(new C00Handshake(5, serveraddress.getIP(), serveraddress.getPort(), EnumConnectionState.STATUS));
networkmanager.scheduleOutboundPacket(new PacketServerTime(data.serverIP));
} catch (Throwable throwable) {
logger.error(throwable);
}
}
public void update(boolean sendPacket) {
synchronized (this.managers) {
Iterator<NetworkManager> iterator = this.managers.keySet().iterator();
while (iterator.hasNext()) {
NetworkManager manager = iterator.next();
if (manager.isChannelOpen()) {
manager.processReceivedPackets();
} else {
iterator.remove();
if (manager.getExitMessage() != null) {
manager.getNetHandler().onDisconnect(manager.getExitMessage());
}
}
}
}
}
@Override
public void close() {
synchronized (this.managers) {
Iterator<NetworkManager> iterator = this.managers.keySet().iterator();
while (iterator.hasNext()) {
NetworkManager networkmanager = (NetworkManager) iterator.next();
if (networkmanager.isChannelOpen()) {
iterator.remove();
networkmanager.closeChannel(new ChatComponentText("Cancelled"));
}
}
}
}
}