func (l *pktHandler) handleLogin(pktHandshake *proto.PacketHandshake) (err, clientErr error) {
	pversion := int(pktHandshake.ProtocolVersion)
	if l.gameInfo.protocolVersion > pversion {
		err = loginErrVersionHigh
		clientErr = err
	}
	if l.gameInfo.protocolVersion < pversion {
		err = loginErrVersionLow
		clientErr = err
	}

	username := pktHandshake.Username
	if !validPlayerUsername.MatchString(username) {
		err = clientErrUsername
		clientErr = err
		return
	}

	log.Print("Client ", l.conn.RemoteAddr(), " connected as ", username)

	//@TODO Allow admins to connect.
	if l.gameInfo.maintenanceMsg != "" {
		err = loginErrorMaintenance
		clientErr = errors.New(l.gameInfo.maintenanceMsg)
		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 = clientErrLoginDenied
		return
	}

	sessionId := fmt.Sprintf("%016x", rand.Int63())
	log.Printf("Player %q has sessionId %s", username, sessionId)

	// @ISSUE #4
	// authenticated, err := l.gameInfo.authserver.Authenticate(sessionId, username)
	// if !authenticated || err != nil {
	//     var reason string
	//     if err != nil {
	//         reason = "Authentication check failed: " + err.Error()
	//     } else {
	//         reason = "Failed authentication"
	//     }
	//     err = fmt.Errorf("Client %v: %s", l.conn.RemoteAddr(), reason)
	//     clientErr = clientErrAuthFailed
	//     return
	// }
	// log.Print("Client ", l.conn.RemoteAddr(), " passed minecraft.net authentication")

	entityId := l.gameInfo.entityManager.NewEntity()

	var playerData nbt.Compound
	if playerData, err = l.gameInfo.game.worldStore.PlayerData(username); err != nil {
		clientErr = clientErrUserData
		return
	}

	player := player.NewPlayer(entityId, l.gameInfo.shardManager, l.conn, username, l.gameInfo.worldStore.SpawnPosition, l.gameInfo.game.playerDisconnect, l.gameInfo.game)
	if playerData != nil {
		if err = player.UnmarshalNbt(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 = clientErrUserData
			return
		}
	}

	l.gameInfo.game.playerConnect <- player
	player.Run()

	return
}
func (l *pktHandler) handleLogin(conn net.Conn) (err, clientErr error) {
	if !validPlayerUsername.MatchString(l.username) {
		err = clientErrUsername
		clientErr = err
		return
	}

	log.Print("Client ", conn.RemoteAddr(), " connected as ", l.username)

	// TODO Allow admins to connect.
	if l.gameInfo.maintenanceMsg != "" {
		err = loginErrorMaintenance
		clientErr = errors.New(l.gameInfo.maintenanceMsg)
		return
	}

	// Load player permissions.
	permissions := gamerules.Permissions.UserPermissions(l.username)
	if !permissions.Has("login") {
		err = fmt.Errorf("Player %q does not have login permission", l.username)
		clientErr = clientErrLoginDenied
		return
	}

	if err = proto.ServerWriteHandshake(conn, l.gameInfo.serverId); err != nil {
		clientErr = clientErrHandshake
		return
	}

	if l.gameInfo.serverId != "-" {
		var authenticated bool
		authenticated, err = l.gameInfo.authserver.Authenticate(l.gameInfo.serverId, l.username)
		if !authenticated || err != nil {
			var reason string
			if err != nil {
				reason = "Authentication check failed: " + err.Error()
			} else {
				reason = "Failed authentication"
			}
			err = fmt.Errorf("Client %v: %s", conn.RemoteAddr(), reason)
			clientErr = clientErrAuthFailed
			return
		}
		log.Print("Client ", conn.RemoteAddr(), " passed minecraft.net authentication")
	}

	err = proto.ServerReadPacketExpect(conn, l, []byte{
		proto.PacketIdLogin,
	})
	if err != nil {
		clientErr = clientErrLoginGeneral
		return
	}

	entityId := l.gameInfo.entityManager.NewEntity()

	var playerData *nbt.Compound
	if playerData, err = l.gameInfo.game.worldStore.PlayerData(l.username); err != nil {
		clientErr = clientErrUserData
		return
	}

	player := player.NewPlayer(entityId, l.gameInfo.shardManager, conn, l.username, l.gameInfo.worldStore.SpawnPosition, l.gameInfo.game.playerDisconnect, l.gameInfo.game)
	if playerData != nil {
		if err = player.UnmarshalNbt(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", l.username, err)
			clientErr = clientErrUserData
			return
		}
	}

	l.gameInfo.game.playerConnect <- player
	player.Run()

	return
}