// Parses messages from the client func (p *MessageParser) CsParse(reader io.Reader, logger *log.Logger) { p.logger = logger // If we return, we should consume all input to avoid blocking the pipe // we're listening on. TODO Maybe we could just close it? defer p.consumeUnrecognizedInput(reader) defer func() { if err := recover(); err != nil { p.printf("Parsing failed: %v", err) } }() username, err := proto.ServerReadHandshake(reader) if err != nil { p.printf("ServerReadHandshake error: %v", err) return } p.printf("ServerReadHandshake(username=%v)", username) loginUsername, err := proto.ServerReadLogin(reader) if err != nil { p.printf("ServerReadLogin error: %v", err) return } p.printf("ServerReadLogin(username=%v)", loginUsername) for { err := proto.ServerReadPacket(reader, p) if err != nil { if err != os.EOF { p.printf("ReceiveLoop failed: %v", err) } else { p.printf("ReceiveLoop hit EOF") } return } } }
// Negotiate a new player client login. This function runs in a new goroutine // for each player connection and therefore should not attempt to alter the // game structure without enqueue(). func (game *Game) login(conn net.Conn) { // TODO: This function should access game in a thread-safe way for reading var err, clientErr os.Error defer func() { if err != nil { log.Print(err.String()) if clientErr == nil { clientErr = os.NewError("Server error.") } proto.WriteDisconnect(conn, clientErr.String()) conn.Close() } }() var username string if username, err = proto.ServerReadHandshake(conn); err != nil { clientErr = os.NewError("Handshake error.") return } if !validPlayerUsername.MatchString(username) { err = os.NewError("Bad username") clientErr = err return } log.Print("Client ", conn.RemoteAddr(), " connected as ", username) if game.UnderMaintenanceMsg != "" { err = fmt.Errorf("Server under maintenance, kicking player: %q", username) clientErr = os.NewError(game.UnderMaintenanceMsg) return } // Load player permissions. permissions := gamerules.Permissions.UserPermissions(username) if !permissions.Has("login") { err = fmt.Errorf("Player %q does not have login permission", username) clientErr = os.NewError("You do not have access to this server.") return } if err = proto.ServerWriteHandshake(conn, game.serverId); err != nil { clientErr = os.NewError("Handshake error.") return } if game.serverId != "-" { var authenticated bool authserver := &server_auth.ServerAuth{"http://www.minecraft.net/game/checkserver.jsp"} authenticated, err = authserver.Authenticate(game.serverId, username) if !authenticated || err != nil { var reason string if err != nil { reason = "Authentication check failed: " + err.String() } else { reason = "Failed authentication" } err = fmt.Errorf("Client %v: %s", conn.RemoteAddr(), reason) clientErr = os.NewError(reason) return } log.Print("Client ", conn.RemoteAddr(), " passed minecraft.net authentication") } if _, err = proto.ServerReadLogin(conn); err != nil { clientErr = os.NewError("Login error.") return } entityId := game.entityManager.NewEntity() var playerData nbt.ITag if playerData, err = game.worldStore.PlayerData(username); err != nil { clientErr = os.NewError("Error reading user data. Please contact the server administrator.") return } player := player.NewPlayer(entityId, game.shardManager, conn, username, game.worldStore.SpawnPosition, game.playerDisconnect, game) if playerData != nil { if err = player.ReadNbt(playerData); err != nil { // Don't let the player log in, as they will only have default inventory // etc., which could lose items from them. Better for an administrator to // sort this out. err = fmt.Errorf("Error parsing player data for %q: %v", username, err) clientErr = os.NewError("Error reading user data. Please contact the server administrator.") return } } game.playerConnect <- player player.Start() }