package main import ( "crypto/rand" "crypto/rsa" "crypto/x509" _ "embed" "fmt" "github.com/Tnze/go-mc/chat" "github.com/Tnze/go-mc/net" pk "github.com/Tnze/go-mc/net/packet" "github.com/google/uuid" "go.jolheiser.com/beaver" ) //go:embed favicon.png var favicon []byte type Server struct { privateKey *rsa.PrivateKey publicKey []byte discordInvite string } func NewServer(discordInvite string) (*Server, error) { private, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil { return nil, fmt.Errorf("error generate private key: %v", err) } public, err := x509.MarshalPKIXPublicKey(private.Public()) if err != nil { return nil, fmt.Errorf("error form public key to PKIX, ASN.1 DER: %v", err) } private.Precompute() return &Server{ privateKey: private, publicKey: public, discordInvite: discordInvite, }, nil } func (s *Server) Start(port int) error { beaver.Infof("Listening on http://localhost:%d", port) l, err := net.ListenMC(fmt.Sprintf(":%d", port)) if err != nil { return fmt.Errorf("listen error: %v", err) } for { conn, err := l.Accept() if err != nil { beaver.Errorf("Accept error: %v", err) } go s.acceptConn(conn) } } func (s *Server) acceptConn(conn net.Conn) { defer conn.Close() // handshake _, intention, err := s.handshake(conn) if err != nil { beaver.Errorf("Handshake error: %v", err) return } switch intention { default: beaver.Errorf("Unknown handshake intention: %v", intention) case 1: s.acceptListPing(conn) case 2: s.handlePlaying(conn) } } func (s *Server) handlePlaying(conn net.Conn) { info, err := s.acceptLogin(conn) if err != nil { beaver.Errorf("login failed: %v", err) return } verify, err := s.encryptionRequest(conn) if err != nil { beaver.Errorf("could not send encryption request: %v", err) return } profile, secret, err := s.encryptionResponse(conn, info.Name, verify) if err != nil { beaver.Errorf("could not get encryption response: %v", err) return } // TODO Register econn, err := encryptedConn(conn, secret) if err != nil { beaver.Errorf("could not create encrypted connection: %v", err) return } msg := fmt.Sprintf("Thanks for registering, %s!", profile.Name) if s.discordInvite != "" { msg += fmt.Sprintf("\n\nJoin the Discord\n%s", s.discordInvite) } packet := pk.Marshal(0x00, chat.Message{Text: msg}, ) if err := packet.Pack(econn, 0); err != nil { beaver.Errorf("could not disconnect player: %v", err) } } type PlayerInfo struct { Name string UUID uuid.UUID OPLevel int } // acceptLogin check player's account func (s *Server) acceptLogin(conn net.Conn) (info PlayerInfo, err error) { //login start var p pk.Packet err = conn.ReadPacket(&p) if err != nil { return } err = p.Scan((*pk.String)(&info.Name)) //decode username as pk.String if err != nil { return } return } // handshake receive and parse Handshake packet func (s *Server) handshake(conn net.Conn) (protocol, intention int32, err error) { var ( p pk.Packet Protocol, Intention pk.VarInt ServerAddress pk.String // ignored ServerPort pk.UnsignedShort // ignored ) // receive handshake packet if err = conn.ReadPacket(&p); err != nil { return } err = p.Scan(&Protocol, &ServerAddress, &ServerPort, &Intention) return int32(Protocol), int32(Intention), err }