func (n *networkManager) Connect(profile mojang.Profile, server string) { logLevel := networkLog.Value() go func() { var err error n.conn, err = protocol.Dial(server) if err != nil { n.SignalClose(err) return } if logLevel > 0 { n.conn.Logger = func(read bool, packet protocol.Packet) { if !read && logLevel < 2 { return } if logLevel < 3 { switch packet.(type) { case *protocol.ChunkData: return } } dir := "read" if !read { dir = "write" } console.Text("%s[%s] %T%+v", server, dir, packet, packet) } } err = n.conn.LoginToServer(profile) if err != nil { n.SignalClose(err) return } preLogin: for { packet, err := n.conn.ReadPacket() if err != nil { n.SignalClose(err) return } switch packet := packet.(type) { case *protocol.SetInitialCompression: n.conn.SetCompression(int(packet.Threshold)) case *protocol.LoginSuccess: n.conn.State = protocol.Play break preLogin case *protocol.LoginDisconnect: n.SignalClose(errors.New(packet.Reason.String())) return default: n.SignalClose(fmt.Errorf("unhandled packet %T", packet)) return } } first := true for { packet, err := n.conn.ReadPacket() if err != nil { n.SignalClose(err) return } if first { go n.writeHandler() first = false } // Handle keep alives async as there is no need to process them switch packet := packet.(type) { case *protocol.KeepAliveClientbound: n.Write(&protocol.KeepAliveServerbound{ID: packet.ID}) case *protocol.SetCompression: n.conn.SetCompression(int(packet.Threshold)) default: n.readChan <- packet } } }() }
func (sl *serverList) pingServer(addr string, motd, version *ui.Formatted, icon *ui.Image, id string, ping *ui.Image, players *ui.Text) { conn, err := protocol.Dial(addr) if err != nil { syncChan <- func() { msg := &format.TextComponent{Text: err.Error()} msg.Color = format.Red motd.Update(format.Wrap(msg)) } return } defer conn.Close() resp, pingTime, err := conn.RequestStatus() syncChan <- func() { if err != nil { msg := &format.TextComponent{Text: err.Error()} msg.Color = format.Red motd.Update(format.Wrap(msg)) return } y := 0.0 pt := pingTime.Seconds() / 1000 switch { case pt <= 75: y = 16 / 256.0 case pt <= 150: y = 24 / 256.0 case pt <= 225: y = 32 / 256.0 case pt <= 350: y = 40 / 256.0 case pt < 999: y = 48 / 256.0 default: y = 56 / 256.0 } ping.SetTextureY(y) if resp.Version.Protocol == protocol.SupportedProtocolVersion { players.SetG(255) players.SetB(255) players.Update(fmt.Sprintf("%d/%d", resp.Players.Online, resp.Players.Max)) } else { players.SetG(85) players.SetB(85) players.Update(fmt.Sprintf("Out of date %d/%d", resp.Players.Online, resp.Players.Max)) } txt := &format.TextComponent{Text: resp.Version.Name} txt.Color = format.Yellow ver := format.Wrap(txt) format.ConvertLegacy(ver) version.Update(ver) desc := resp.Description format.ConvertLegacy(desc) motd.Update(desc) if strings.HasPrefix(resp.Favicon, "data:image/png;base64,") { favicon := resp.Favicon[len("data:image/png;base64,"):] data, err := base64.StdEncoding.DecodeString(favicon) if err != nil { fmt.Printf("error base64 decoding favicon: %s\n", err) return } img, err := png.Decode(bytes.NewReader(data)) if err != nil { fmt.Printf("error decoding favicon: %s\n", err) return } render.AddIcon(id, img) icon.SetTexture(render.Icon(id)) } } }