    001package net.minecraft.client.gui;
    002
    003import cpw.mods.fml.relauncher.Side;
    004import cpw.mods.fml.relauncher.SideOnly;
    005import java.io.DataInputStream;
    006import java.io.DataOutputStream;
    007import java.io.IOException;
    008import java.net.InetSocketAddress;
    009import java.net.Socket;
    010import java.util.Collections;
    011import java.util.List;
    012import net.minecraft.client.multiplayer.GuiConnecting;
    013import net.minecraft.client.multiplayer.LanServer;
    014import net.minecraft.client.multiplayer.LanServerList;
    015import net.minecraft.client.multiplayer.ServerAddress;
    016import net.minecraft.client.multiplayer.ServerData;
    017import net.minecraft.client.multiplayer.ServerList;
    018import net.minecraft.client.multiplayer.ThreadLanServerFind;
    019import net.minecraft.network.packet.Packet;
    020import net.minecraft.util.ChatAllowedCharacters;
    021import net.minecraft.util.EnumChatFormatting;
    022import net.minecraft.util.MathHelper;
    023import net.minecraft.util.StatCollector;
    024import net.minecraft.util.StringTranslate;
    025import org.lwjgl.input.Keyboard;
    026
    027@SideOnly(Side.CLIENT)
    028public class GuiMultiplayer extends GuiScreen
    029{
    030    /** Number of outstanding ThreadPollServers threads */
    031    private static int threadsPending = 0;
    032
    033    /** Lock object for use with synchronized() */
    034    private static Object lock = new Object();
    035
    036    /**
    037     * A reference to the screen object that created this. Used for navigating between screens.
    038     */
    039    private GuiScreen parentScreen;
    040
    041    /** Slot container for the server list */
    042    private GuiSlotServer serverSlotContainer;
    043    private ServerList internetServerList;
    044
    045    /** Index of the currently selected server */
    046    private int selectedServer = -1;
    047    private GuiButton field_96289_p;
    048
    049    /** The 'Join Server' button */
    050    private GuiButton buttonSelect;
    051
    052    /** The 'Delete' button */
    053    private GuiButton buttonDelete;
    054
    055    /** The 'Delete' button was clicked */
    056    private boolean deleteClicked = false;
    057
    058    /** The 'Add server' button was clicked */
    059    private boolean addClicked = false;
    060
    061    /** The 'Edit' button was clicked */
    062    private boolean editClicked = false;
    063
    064    /** The 'Direct Connect' button was clicked */
    065    private boolean directClicked = false;
    066
    067    /** This GUI's lag tooltip text or null if no lag icon is being hovered. */
    068    private String lagTooltip = null;
    069
    070    /** Instance of ServerData. */
    071    private ServerData theServerData = null;
    072    private LanServerList localNetworkServerList;
    073    private ThreadLanServerFind localServerFindThread;
    074
    075    /** How many ticks this Gui is already opened */
    076    private int ticksOpened;
    077    private boolean field_74024_A;
    078    private List listofLanServers = Collections.emptyList();
    079
    080    public GuiMultiplayer(GuiScreen par1GuiScreen)
    081    {
    082        this.parentScreen = par1GuiScreen;
    083    }
    084
    085    /**
    086     * Adds the buttons (and other controls) to the screen in question.
    087     */
    088    public void initGui()
    089    {
    090        Keyboard.enableRepeatEvents(true);
    091        this.buttonList.clear();
    092
    093        if (!this.field_74024_A)
    094        {
    095            this.field_74024_A = true;
    096            this.internetServerList = new ServerList(this.mc);
    097            this.internetServerList.loadServerList();
    098            this.localNetworkServerList = new LanServerList();
    099
    100            try
    101            {
    102                this.localServerFindThread = new ThreadLanServerFind(this.localNetworkServerList);
    103                this.localServerFindThread.start();
    104            }
    105            catch (Exception exception)
    106            {
    107                this.mc.getLogAgent().logWarning("Unable to start LAN server detection: " + exception.getMessage());
    108            }
    109
    110            this.serverSlotContainer = new GuiSlotServer(this);
    111        }
    112        else
    113        {
    114            this.serverSlotContainer.func_77207_a(this.width, this.height, 32, this.height - 64);
    115        }
    116
    117        this.initGuiControls();
    118    }
    119
    120    /**
    121     * Populate the GuiScreen controlList
    122     */
    123    public void initGuiControls()
    124    {
    125        StringTranslate stringtranslate = StringTranslate.getInstance();
    126        this.buttonList.add(this.field_96289_p = new GuiButton(7, this.width / 2 - 154, this.height - 28, 70, 20, stringtranslate.translateKey("selectServer.edit")));
    127        this.buttonList.add(this.buttonDelete = new GuiButton(2, this.width / 2 - 74, this.height - 28, 70, 20, stringtranslate.translateKey("selectServer.delete")));
    128        this.buttonList.add(this.buttonSelect = new GuiButton(1, this.width / 2 - 154, this.height - 52, 100, 20, stringtranslate.translateKey("selectServer.select")));
    129        this.buttonList.add(new GuiButton(4, this.width / 2 - 50, this.height - 52, 100, 20, stringtranslate.translateKey("selectServer.direct")));
    130        this.buttonList.add(new GuiButton(3, this.width / 2 + 4 + 50, this.height - 52, 100, 20, stringtranslate.translateKey("selectServer.add")));
    131        this.buttonList.add(new GuiButton(8, this.width / 2 + 4, this.height - 28, 70, 20, stringtranslate.translateKey("selectServer.refresh")));
    132        this.buttonList.add(new GuiButton(0, this.width / 2 + 4 + 76, this.height - 28, 75, 20, stringtranslate.translateKey("gui.cancel")));
    133        boolean flag = this.selectedServer >= 0 && this.selectedServer < this.serverSlotContainer.getSize();
    134        this.buttonSelect.enabled = flag;
    135        this.field_96289_p.enabled = flag;
    136        this.buttonDelete.enabled = flag;
    137    }
    138
    139    /**
    140     * Called from the main game loop to update the screen.
    141     */
    142    public void updateScreen()
    143    {
    144        super.updateScreen();
    145        ++this.ticksOpened;
    146
    147        if (this.localNetworkServerList.getWasUpdated())
    148        {
    149            this.listofLanServers = this.localNetworkServerList.getLanServers();
    150            this.localNetworkServerList.setWasNotUpdated();
    151        }
    152    }
    153
    154    /**
    155     * Called when the screen is unloaded. Used to disable keyboard repeat events
    156     */
    157    public void onGuiClosed()
    158    {
    159        Keyboard.enableRepeatEvents(false);
    160
    161        if (this.localServerFindThread != null)
    162        {
    163            this.localServerFindThread.interrupt();
    164            this.localServerFindThread = null;
    165        }
    166    }
    167
    168    /**
    169     * Fired when a control is clicked. This is the equivalent of ActionListener.actionPerformed(ActionEvent e).
    170     */
    171    protected void actionPerformed(GuiButton par1GuiButton)
    172    {
    173        if (par1GuiButton.enabled)
    174        {
    175            if (par1GuiButton.id == 2)
    176            {
    177                String s = this.internetServerList.getServerData(this.selectedServer).serverName;
    178
    179                if (s != null)
    180                {
    181                    this.deleteClicked = true;
    182                    StringTranslate stringtranslate = StringTranslate.getInstance();
    183                    String s1 = stringtranslate.translateKey("selectServer.deleteQuestion");
    184                    String s2 = "\'" + s + "\' " + stringtranslate.translateKey("selectServer.deleteWarning");
    185                    String s3 = stringtranslate.translateKey("selectServer.deleteButton");
    186                    String s4 = stringtranslate.translateKey("gui.cancel");
    187                    GuiYesNo guiyesno = new GuiYesNo(this, s1, s2, s3, s4, this.selectedServer);
    188                    this.mc.displayGuiScreen(guiyesno);
    189                }
    190            }
    191            else if (par1GuiButton.id == 1)
    192            {
    193                this.joinServer(this.selectedServer);
    194            }
    195            else if (par1GuiButton.id == 4)
    196            {
    197                this.directClicked = true;
    198                this.mc.displayGuiScreen(new GuiScreenServerList(this, this.theServerData = new ServerData(StatCollector.translateToLocal("selectServer.defaultName"), "")));
    199            }
    200            else if (par1GuiButton.id == 3)
    201            {
    202                this.addClicked = true;
    203                this.mc.displayGuiScreen(new GuiScreenAddServer(this, this.theServerData = new ServerData(StatCollector.translateToLocal("selectServer.defaultName"), "")));
    204            }
    205            else if (par1GuiButton.id == 7)
    206            {
    207                this.editClicked = true;
    208                ServerData serverdata = this.internetServerList.getServerData(this.selectedServer);
    209                this.theServerData = new ServerData(serverdata.serverName, serverdata.serverIP);
    210                this.theServerData.setHideAddress(serverdata.isHidingAddress());
    211                this.mc.displayGuiScreen(new GuiScreenAddServer(this, this.theServerData));
    212            }
    213            else if (par1GuiButton.id == 0)
    214            {
    215                this.mc.displayGuiScreen(this.parentScreen);
    216            }
    217            else if (par1GuiButton.id == 8)
    218            {
    219                this.mc.displayGuiScreen(new GuiMultiplayer(this.parentScreen));
    220            }
    221            else
    222            {
    223                this.serverSlotContainer.actionPerformed(par1GuiButton);
    224            }
    225        }
    226    }
    227
    228    public void confirmClicked(boolean par1, int par2)
    229    {
    230        if (this.deleteClicked)
    231        {
    232            this.deleteClicked = false;
    233
    234            if (par1)
    235            {
    236                this.internetServerList.removeServerData(par2);
    237                this.internetServerList.saveServerList();
    238                this.selectedServer = -1;
    239            }
    240
    241            this.mc.displayGuiScreen(this);
    242        }
    243        else if (this.directClicked)
    244        {
    245            this.directClicked = false;
    246
    247            if (par1)
    248            {
    249                this.connectToServer(this.theServerData);
    250            }
    251            else
    252            {
    253                this.mc.displayGuiScreen(this);
    254            }
    255        }
    256        else if (this.addClicked)
    257        {
    258            this.addClicked = false;
    259
    260            if (par1)
    261            {
    262                this.internetServerList.addServerData(this.theServerData);
    263                this.internetServerList.saveServerList();
    264                this.selectedServer = -1;
    265            }
    266
    267            this.mc.displayGuiScreen(this);
    268        }
    269        else if (this.editClicked)
    270        {
    271            this.editClicked = false;
    272
    273            if (par1)
    274            {
    275                ServerData serverdata = this.internetServerList.getServerData(this.selectedServer);
    276                serverdata.serverName = this.theServerData.serverName;
    277                serverdata.serverIP = this.theServerData.serverIP;
    278                serverdata.setHideAddress(this.theServerData.isHidingAddress());
    279                this.internetServerList.saveServerList();
    280            }
    281
    282            this.mc.displayGuiScreen(this);
    283        }
    284    }
    285
    286    /**
    287     * Fired when a key is typed. This is the equivalent of KeyListener.keyTyped(KeyEvent e).
    288     */
    289    protected void keyTyped(char par1, int par2)
    290    {
    291        int j = this.selectedServer;
    292
    293        if (par2 == 59)
    294        {
    295            this.mc.gameSettings.hideServerAddress = !this.mc.gameSettings.hideServerAddress;
    296            this.mc.gameSettings.saveOptions();
    297        }
    298        else
    299        {
    300            if (isShiftKeyDown() && par2 == 200)
    301            {
    302                if (j > 0 && j < this.internetServerList.countServers())
    303                {
    304                    this.internetServerList.swapServers(j, j - 1);
    305                    --this.selectedServer;
    306
    307                    if (j < this.internetServerList.countServers() - 1)
    308                    {
    309                        this.serverSlotContainer.func_77208_b(-this.serverSlotContainer.slotHeight);
    310                    }
    311                }
    312            }
    313            else if (isShiftKeyDown() && par2 == 208)
    314            {
    315                if (j < this.internetServerList.countServers() - 1)
    316                {
    317                    this.internetServerList.swapServers(j, j + 1);
    318                    ++this.selectedServer;
    319
    320                    if (j > 0)
    321                    {
    322                        this.serverSlotContainer.func_77208_b(this.serverSlotContainer.slotHeight);
    323                    }
    324                }
    325            }
    326            else if (par1 == 13)
    327            {
    328                this.actionPerformed((GuiButton)this.buttonList.get(2));
    329            }
    330            else
    331            {
    332                super.keyTyped(par1, par2);
    333            }
    334        }
    335    }
    336
    337    /**
    338     * Draws the screen and all the components in it.
    339     */
    340    public void drawScreen(int par1, int par2, float par3)
    341    {
    342        this.lagTooltip = null;
    343        StringTranslate stringtranslate = StringTranslate.getInstance();
    344        this.drawDefaultBackground();
    345        this.serverSlotContainer.drawScreen(par1, par2, par3);
    346        this.drawCenteredString(this.fontRenderer, stringtranslate.translateKey("multiplayer.title"), this.width / 2, 20, 16777215);
    347        super.drawScreen(par1, par2, par3);
    348
    349        if (this.lagTooltip != null)
    350        {
    351            this.func_74007_a(this.lagTooltip, par1, par2);
    352        }
    353    }
    354
    355    /**
    356     * Join server by slot index
    357     */
    358    private void joinServer(int par1)
    359    {
    360        if (par1 < this.internetServerList.countServers())
    361        {
    362            this.connectToServer(this.internetServerList.getServerData(par1));
    363        }
    364        else
    365        {
    366            par1 -= this.internetServerList.countServers();
    367
    368            if (par1 < this.listofLanServers.size())
    369            {
    370                LanServer lanserver = (LanServer)this.listofLanServers.get(par1);
    371                this.connectToServer(new ServerData(lanserver.getServerMotd(), lanserver.getServerIpPort()));
    372            }
    373        }
    374    }
    375
    376    private void connectToServer(ServerData par1ServerData)
    377    {
    378        this.mc.displayGuiScreen(new GuiConnecting(this, this.mc, par1ServerData));
    379    }
    380
    381    private static void func_74017_b(ServerData par1ServerData) throws IOException
    382    {
    383        ServerAddress serveraddress = ServerAddress.func_78860_a(par1ServerData.serverIP);
    384        Socket socket = null;
    385        DataInputStream datainputstream = null;
    386        DataOutputStream dataoutputstream = null;
    387
    388        try
    389        {
    390            socket = new Socket();
    391            socket.setSoTimeout(3000);
    392            socket.setTcpNoDelay(true);
    393            socket.setTrafficClass(18);
    394            socket.connect(new InetSocketAddress(serveraddress.getIP(), serveraddress.getPort()), 3000);
    395            datainputstream = new DataInputStream(socket.getInputStream());
    396            dataoutputstream = new DataOutputStream(socket.getOutputStream());
    397            dataoutputstream.write(254);
    398            dataoutputstream.write(1);
    399
    400            if (datainputstream.read() != 255)
    401            {
    402                throw new IOException("Bad message");
    403            }
    404
    405            String s = Packet.readString(datainputstream, 256);
    406            char[] achar = s.toCharArray();
    407
    408            for (int i = 0; i < achar.length; ++i)
    409            {
    410                if (achar[i] != 167 && achar[i] != 0 && ChatAllowedCharacters.allowedCharacters.indexOf(achar[i]) < 0)
    411                {
    412                    achar[i] = 63;
    413                }
    414            }
    415
    416            s = new String(achar);
    417            int j;
    418            int k;
    419            String[] astring;
    420
    421            if (s.startsWith("\u00a7") && s.length() > 1)
    422            {
    423                astring = s.substring(1).split("\u0000");
    424
    425                if (MathHelper.parseIntWithDefault(astring[0], 0) == 1)
    426                {
    427                    par1ServerData.serverMOTD = astring[3];
    428                    par1ServerData.field_82821_f = MathHelper.parseIntWithDefault(astring[1], par1ServerData.field_82821_f);
    429                    par1ServerData.gameVersion = astring[2];
    430                    j = MathHelper.parseIntWithDefault(astring[4], 0);
    431                    k = MathHelper.parseIntWithDefault(astring[5], 0);
    432
    433                    if (j >= 0 && k >= 0)
    434                    {
    435                        par1ServerData.populationInfo = EnumChatFormatting.GRAY + "" + j + "" + EnumChatFormatting.DARK_GRAY + "/" + EnumChatFormatting.GRAY + k;
    436                    }
    437                    else
    438                    {
    439                        par1ServerData.populationInfo = "" + EnumChatFormatting.DARK_GRAY + "???";
    440                    }
    441                }
    442                else
    443                {
    444                    par1ServerData.gameVersion = "???";
    445                    par1ServerData.serverMOTD = "" + EnumChatFormatting.DARK_GRAY + "???";
    446                    par1ServerData.field_82821_f = 61;
    447                    par1ServerData.populationInfo = "" + EnumChatFormatting.DARK_GRAY + "???";
    448                }
    449            }
    450            else
    451            {
    452                astring = s.split("\u00a7");
    453                s = astring[0];
    454                j = -1;
    455                k = -1;
    456
    457                try
    458                {
    459                    j = Integer.parseInt(astring[1]);
    460                    k = Integer.parseInt(astring[2]);
    461                }
    462                catch (Exception exception)
    463                {
    464                    ;
    465                }
    466
    467                par1ServerData.serverMOTD = EnumChatFormatting.GRAY + s;
    468
    469                if (j >= 0 && k > 0)
    470                {
    471                    par1ServerData.populationInfo = EnumChatFormatting.GRAY + "" + j + "" + EnumChatFormatting.DARK_GRAY + "/" + EnumChatFormatting.GRAY + k;
    472                }
    473                else
    474                {
    475                    par1ServerData.populationInfo = "" + EnumChatFormatting.DARK_GRAY + "???";
    476                }
    477
    478                par1ServerData.gameVersion = "1.3";
    479                par1ServerData.field_82821_f = 59;
    480            }
    481        }
    482        finally
    483        {
    484            try
    485            {
    486                if (datainputstream != null)
    487                {
    488                    datainputstream.close();
    489                }
    490            }
    491            catch (Throwable throwable)
    492            {
    493                ;
    494            }
    495
    496            try
    497            {
    498                if (dataoutputstream != null)
    499                {
    500                    dataoutputstream.close();
    501                }
    502            }
    503            catch (Throwable throwable1)
    504            {
    505                ;
    506            }
    507
    508            try
    509            {
    510                if (socket != null)
    511                {
    512                    socket.close();
    513                }
    514            }
    515            catch (Throwable throwable2)
    516            {
    517                ;
    518            }
    519        }
    520    }
    521
    522    protected void func_74007_a(String par1Str, int par2, int par3)
    523    {
    524        if (par1Str != null)
    525        {
    526            int k = par2 + 12;
    527            int l = par3 - 12;
    528            int i1 = this.fontRenderer.getStringWidth(par1Str);
    529            this.drawGradientRect(k - 3, l - 3, k + i1 + 3, l + 8 + 3, -1073741824, -1073741824);
    530            this.fontRenderer.drawStringWithShadow(par1Str, k, l, -1);
    531        }
    532    }
    533
    534    static ServerList getInternetServerList(GuiMultiplayer par0GuiMultiplayer)
    535    {
    536        return par0GuiMultiplayer.internetServerList;
    537    }
    538
    539    static List getListOfLanServers(GuiMultiplayer par0GuiMultiplayer)
    540    {
    541        return par0GuiMultiplayer.listofLanServers;
    542    }
    543
    544    static int getSelectedServer(GuiMultiplayer par0GuiMultiplayer)
    545    {
    546        return par0GuiMultiplayer.selectedServer;
    547    }
    548
    549    static int getAndSetSelectedServer(GuiMultiplayer par0GuiMultiplayer, int par1)
    550    {
    551        return par0GuiMultiplayer.selectedServer = par1;
    552    }
    553
    554    /**
    555     * Return buttonSelect GuiButton
    556     */
    557    static GuiButton getButtonSelect(GuiMultiplayer par0GuiMultiplayer)
    558    {
    559        return par0GuiMultiplayer.buttonSelect;
    560    }
    561
    562    /**
    563     * Return buttonEdit GuiButton
    564     */
    565    static GuiButton getButtonEdit(GuiMultiplayer par0GuiMultiplayer)
    566    {
    567        return par0GuiMultiplayer.field_96289_p;
    568    }
    569
    570    /**
    571     * Return buttonDelete GuiButton
    572     */
    573    static GuiButton getButtonDelete(GuiMultiplayer par0GuiMultiplayer)
    574    {
    575        return par0GuiMultiplayer.buttonDelete;
    576    }
    577
    578    static void func_74008_b(GuiMultiplayer par0GuiMultiplayer, int par1)
    579    {
    580        par0GuiMultiplayer.joinServer(par1);
    581    }
    582
    583    static int getTicksOpened(GuiMultiplayer par0GuiMultiplayer)
    584    {
    585        return par0GuiMultiplayer.ticksOpened;
    586    }
    587
    588    /**
    589     * Returns the lock object for use with synchronized()
    590     */
    591    static Object getLock()
    592    {
    593        return lock;
    594    }
    595
    596    static int getThreadsPending()
    597    {
    598        return threadsPending;
    599    }
    600
    601    static int increaseThreadsPending()
    602    {
    603        return threadsPending++;
    604    }
    605
    606    static void func_82291_a(ServerData par0ServerData) throws IOException
    607    {
    608        func_74017_b(par0ServerData);
    609    }
    610
    611    static int decreaseThreadsPending()
    612    {
    613        return threadsPending--;
    614    }
    615
    616    static String getAndSetLagTooltip(GuiMultiplayer par0GuiMultiplayer, String par1Str)
    617    {
    618        return par0GuiMultiplayer.lagTooltip = par1Str;
    619    }
    620}